ESP-IDF
Install, configure, and verify Honch in ESP32 firmware built with Espressif's ESP-IDF.
The ESP-IDF port is the production Honch SDK for ESP32-family chips. It is a thin wrapper over the shared core, registered as an ESP-IDF component and published to the Espressif Component Registry as honch/honch.
Status
0.3.0. Requires ESP-IDF >= 5.0 (verified against v6.0.1).Before You Start
- The SDK does no network setup of its own. Your firmware brings up NVS, the network interface, Wi-Fi, time (SNTP), and the TLS trust store.
- There is no background task. You pump delivery by calling
honch_tick()from a FreeRTOS task you create. Allow at least 8192 bytes of stack for that task — the TLS handshake needs the headroom. - The default queue is in RAM and clears on reset or power loss. See Queue And Durability for persistence.
1. Install The Component
Add the dependency with the component manager:
idf.py add-dependency "honch/honch^0.3.0"Or vendor the SDK as a git submodule (the component pulls in the shared core/, so submodule the whole repository):
git submodule add https://github.com/honch-io/SDK.git components/honch2. Configure And Initialize
honch_init() validates its config, derives identity, reconciles the queue, and queues $device_boot. It is synchronous and performs no network I/O.
#include "honch.h"
static uint8_t honch_event_buffer[16384];
void app_main(void) {
// Bring up NVS, netif, Wi-Fi, and SNTP first (your firmware's job).
honch_config_t config = {
.api_key = CONFIG_HONCH_API_KEY,
.host = "https://i.honch.io",
.device_model = "demo-board",
.firmware_version = "1.0.0",
.event_buffer = honch_event_buffer,
.event_buffer_size = sizeof(honch_event_buffer),
};
honch_err_t err = honch_init(&config);
if (err != HONCH_OK) {
ESP_LOGE("app", "honch_init failed: %d", err);
return;
}
}Required fields are api_key, host, device_model, firmware_version, and an event buffer (event_buffer + event_buffer_size) unless you supply your own event_queue_ops. Recommended buffer size is 8192 bytes or more.
| Field | Default | Notes |
|---|---|---|
environment | "production" | Set to development/staging to separate test data. |
flush_interval_seconds | 120 | Periodic flush cadence. |
flush_event_threshold | 20 | Queue depth that requests a flush. |
flush_min_interval_ms | 15000 | Minimum spacing between upload attempts. |
transport_timeout_ms | 8000 | Clamped to 1000–10000 ms. A constrained ESP32 TLS handshake plus the POST needs headroom above the old 2.5 s. |
flush_max_batches | 1 | Batches sent per flush. |
battery_callback / battery_low_threshold | — / 15 | Return 0–100 to enable $battery_level and $battery_low. |
connectivity_callback | — | Return 0 when offline so the SDK skips DNS/TLS. Defaults to always-connected. |
enable_error_tracking | false | Master switch for crash + error reporting: emit a $crash after an abnormal reset and capture ESP_LOGE lines as $error. See Crash And Error Tracking. |
enable_crash_symbolication | false | When a crash is reported, also capture the full coredump for backend symbolication (a function/file/line backtrace) and stamp the firmware build ID. Requires enable_error_tracking. |
state_storage_ops / event_queue_ops | — | Supply durable identity / a durable queue. |
If you do not set a device_id, the SDK derives a stable one from the Wi-Fi station MAC, formatted esp32-<MAC>.
3. Pump Delivery
Create a low-priority task that calls honch_tick() on an interval. tick() sends at most one chunk per call and blocks up to transport_timeout_ms, so keep it off any latency-sensitive path.
static void honch_task(void *arg) {
for (;;) {
honch_err_t err = honch_tick();
if (err != HONCH_OK && err != HONCH_ERR_TRANSPORT &&
err != HONCH_ERR_TIMEOUT && err != HONCH_ERR_OFFLINE) {
ESP_LOGW("honch", "tick: %d", err);
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// xTaskCreate(honch_task, "honch", 8192, NULL, 2, NULL);4. Track Events, Identity, And Sessions
const honch_property_t props[] = {
honch_prop("mode", honch_str("record")),
honch_prop("duration_ms", honch_i64(4200)),
};
honch_track("mode_changed", props, 2);
honch_identify("user-123", NULL, 0);
honch_session_start("recording");
honch_track("frame_captured", NULL, 0);
honch_session_end();
honch_flush(); // force a send nowReusing a reserved key (any $-prefixed key, or distinct_id) in your properties returns HONCH_ERR_INVALID_ARG.
5. Track GPIO Safely
There is no GPIO helper in the SDK — and you should not call honch_track() from an ISR, since it can allocate and block. The supported pattern is host-owned: the ISR only hands a pin number to a FreeRTOS queue, and a normal task drains the queue and tracks the event. This is exactly what the example_gpio example does.
static QueueHandle_t s_gpio_queue; // xQueueCreate(16, sizeof(uint32_t))
static void IRAM_ATTR button_isr(void *arg) {
uint32_t pin = (uint32_t)(uintptr_t)arg;
xQueueSendFromISR(s_gpio_queue, &pin, NULL);
}
static void gpio_task(void *arg) {
uint32_t pin;
for (;;) {
if (xQueueReceive(s_gpio_queue, &pin, portMAX_DELAY) == pdTRUE) {
const honch_property_t props[] = { honch_prop("pin", honch_i64(pin)) };
honch_track("button_pressed", props, 1);
}
}
}Debounce and pin setup are your firmware's responsibility, exactly as with any other GPIO work.
Configuration (Kconfig)
Two build-time options gate the error-tracking code, both enabled by default in menuconfig under Honch SDK:
| Option | Default | Effect |
|---|---|---|
CONFIG_HONCH_ERROR_TRACKING | on | Compiles in the crash ($crash) + error-log ($error) capture path. |
CONFIG_HONCH_CRASH_SYMBOLICATION | on | Compiles in coredump capture: stamps the firmware build ID and streams the raw ELF coredump for backend symbolication. Depends on error tracking. |
These control whether the code is compiled in. To actually report at runtime you must also set enable_error_tracking = true (and enable_crash_symbolication = true for coredumps) in your config. The runtime switch gates both the $crash snapshot and the ESP_LOGE → $error capture, so turning it off silences both.
Crash And Error Tracking
With enable_error_tracking on, the SDK does the following automatically — there are no manual reporting calls:
- Crash detection. During init it reads the reset reason and, for an abnormal reset (panic, watchdog, brownout, and similar), queues a
$crashcarrying the reset cause and acrash_id. Because it is re-derived from the hardware reset reason on the next boot, the$crashdoes not depend on the queue surviving the reset. - Error-log capture. It chains
ESP_LOGEso error-level logs become bounded, de-duplicated$errorevents. The runtime switch gates this too — turningenable_error_trackingoff stops both the$crashsnapshot and log capture. - Coredump capture (with
enable_crash_symbolication). When ESP-IDF's coredump-to-flash is configured (CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH, with acoredumppartition), a panic writes a full coredump to flash. On the next boot the SDK streams it to Capture in CRC-checked, resumable chunks; the backend symbolicates it against your firmware's debug symbols into a function/file/line backtrace, joined to the$crashbycrash_id. The on-flash copy is erased only after the upload is acknowledged.
Symbolicated backtraces are produced for Xtensa targets (the original ESP32 and S-series). RISC-V targets (C3/C6/H2) capture the crash but backtrace resolution is more limited.
Transport Contract
Each upload is:
POST <host>/capture
Content-Type: application/vnd.honch.chunk
X-Honch-Project-Key: <api_key>
X-Honch-Stream-Id: <stream_id>TLS uses the ESP-IDF certificate bundle for server verification; there is no option to disable verification. A Retry-After response header is honored. Configure host with https:// in production — the examples use an http:// host only to talk to a local capture service.
Queue And Durability
By default events live in the RAM buffer you provide, capped at 1000 queued entries with drop-oldest eviction. Nothing is persisted, so a reset or power loss clears the queue and any identity you have not made durable.
To survive restarts, supply your own adapters:
state_storage_ops— durable identity (distinct_id) and firmware version. A common backing is NVS. (Note NVS keys are capped at 15 characters, so firmware version is stored underfw_version.)event_queue_ops— a durable event queue in place of the RAM queue.
The SDK ships the hooks; you provide the storage backend.
Verify On Hardware
idf.py set-target esp32s3
idf.py build flash monitorWatch the serial log for $device_boot queuing at init, then a POST /capture from your tick task once Wi-Fi and time are up. A 204 means the batch was accepted; a 202 means a chunk was stored and more follow. See Troubleshooting if events queue but never upload.
Debugging Failures
The esp_http_client transport records structured detail for every failed upload: the HTTP status maps to a reason (e.g. 401 → auth_invalid_key), and an esp_err_t connect/DNS/TLS failure maps into reason with the raw code in os_error. Read it with honch_get_last_error(&detail). The core also emits one deduped line per distinct failure through the platform log hook under the honch tag — ESP_LOGW for retryable, ESP_LOGE for terminal — so idf.py monitor shows, for example:
E (45231) honch: rejected: HTTP 401 - API key invalid or revoked (reason=auth_invalid_key)Because the line is tagged honch, it is skipped by the ESP_LOGE → $error capture hook (no feedback loop). See Error Context & Diagnostics.
Public API
| Function | Purpose |
|---|---|
honch_init(&config) | Initialize the singleton; synchronous, no network. |
honch_get_last_error(&detail) | Structured detail (reason, http_status, os_error) for the last failure. |
honch_track(event, props, count) | Queue an event. |
honch_identify(distinct_id, traits, count) | Set identity; emits $identify. |
honch_set_property(key, value) | Emit $set_property. |
honch_session_start(name) / honch_session_end() | Bracket a session. |
honch_tick() | Cooperative delivery step (one chunk). |
honch_flush() | Send queued batches now. |
honch_reset() | Clear identity, session, and queue. |
honch_shutdown() | Emit $device_shutdown, final flush, tear down. |
honch_get_device_id() | Borrowed pointer to the device ID. |
honch_get_queue_stats(&stats) | Read queue depth and counters. |
All calls return honch_err_t (HONCH_OK on success).