GitHub

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

Stable · 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/honch

2. 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.

FieldDefaultNotes
environment"production"Set to development/staging to separate test data.
flush_interval_seconds120Periodic flush cadence.
flush_event_threshold20Queue depth that requests a flush.
flush_min_interval_ms15000Minimum spacing between upload attempts.
transport_timeout_ms8000Clamped to 1000–10000 ms. A constrained ESP32 TLS handshake plus the POST needs headroom above the old 2.5 s.
flush_max_batches1Batches sent per flush.
battery_callback / battery_low_threshold— / 15Return 0–100 to enable $battery_level and $battery_low.
connectivity_callbackReturn 0 when offline so the SDK skips DNS/TLS. Defaults to always-connected.
enable_error_trackingfalseMaster 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_symbolicationfalseWhen 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_opsSupply 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 now

Reusing 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:

OptionDefaultEffect
CONFIG_HONCH_ERROR_TRACKINGonCompiles in the crash ($crash) + error-log ($error) capture path.
CONFIG_HONCH_CRASH_SYMBOLICATIONonCompiles 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 $crash carrying the reset cause and a crash_id. Because it is re-derived from the hardware reset reason on the next boot, the $crash does not depend on the queue surviving the reset.
  • Error-log capture. It chains ESP_LOGE so error-level logs become bounded, de-duplicated $error events. The runtime switch gates this too — turning enable_error_tracking off stops both the $crash snapshot 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 a coredump partition), 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 $crash by crash_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 under fw_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 monitor

Watch 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. 401auth_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

FunctionPurpose
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).

honch.

Product analytics for consumer hardware.