Skip to content

nimconfig.h: operator precedence bug causes hard crash on ESP32-C3 with arduino-esp32 3.3.7 / ESP-IDF 5.x #1098

@toptensoftware

Description

@toptensoftware

Environment

  • NimBLE-Arduino: 2.3.7
  • arduino-esp32: 3.3.7 (ESP-IDF 5.3.x)
  • Board: ESP32-C3 (RISC-V)

Description

On ESP32-C3 with arduino-esp32 3.3.7, NimBLEDevice::init() causes a hard crash immediately at boot:

Guru Meditation Error: Core 0 panic'ed (Instruction access fault)
MEPC : 0x00000000

This is separate from the btInUse() issue fixed in PR #1090 — that fix is also required, but even with it applied the
crash persists due to the bug described here.

Root cause

nimconfig.h has an operator precedence bug in the block that sets CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE. Because &&
binds more tightly than ||, the !defined() guard only applies to the CONFIG_IDF_TARGET_ESP32 term — the ESP32-C3 and
ESP32-S3 terms are unconditionally true regardless:

// Parsed as: (!defined(VHCI) && defined(ESP32)) || defined(ESP32C3) || defined(ESP32S3)
// For ESP32-C3: always true — !defined() guard is never evaluated
#if !defined(CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE) && defined(CONFIG_IDF_TARGET_ESP32) || \
defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
#define CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE 1
#endif

With CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE=1, NimBLEDevice::init() calls esp_nimble_hci_init() (in esp_nimble_hci.c),
which calls esp_vhci_host_register_callback(). On ESP32-C3 with ESP-IDF 5.x the VHCI API no longer exists — the symbol
is a null weak reference resolving to 0x00000000 — so the CPU jumps to address zero and faults.

This can be confirmed with riscv32-esp-elf-nm against the arduino-esp32 3.3.7 ESP32-C3 libraries:
esp_vhci_host_register_callback is absent from all of them.

Fix

Two changes to src/nimconfig.h:

  1. Add #include "esp_idf_version.h" in the #ifdef ESP_PLATFORM block (after sdkconfig.h) so ESP_IDF_VERSION_VAL is
    available.

  2. Replace the buggy block with correct precedence and an IDF version check (since ESP32-C3/S3 with IDF ≥ 5.0 use
    nimble_port_init() for HCI — there is no VHCI):

    #if !defined(CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE)
    #if defined(CONFIG_IDF_TARGET_ESP32) || \
        ((defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)) && \
          ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0))
    #define CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE 1
    #else
    #define CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE 0
    #endif
    #endif

This preserves the existing behaviour for classic ESP32 and for ESP32-C3/S3 on older IDF, while correctly disabling
VHCI for ESP32-C3/S3 on IDF 5.x.

Relationship to PR #1090

PR #1090 (merged Feb 2026) fixed the related btInUse() issue introduced in arduino-esp32 3.3.7. Both fixes are
required for ESP32-C3 with arduino-esp32 3.3.7: the btInUse() fix prevents BT controller memory being released before
NimBLE starts; this fix prevents the subsequent null-pointer call into the removed VHCI API.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions