Skip to content

[Regression] iOS BLE Connection Drop (Error 531) on DIY Targets: ESP-IDF 5.4 upgrade + Strict Param Enforcement #266

@CaTeIM

Description

@CaTeIM

Hi @jgriffiths and @JamieDriver,

I am providing a comprehensive root cause analysis for the connectivity issues affecting DIY Jade devices (TTGO/ESP32/ESP32S3) on iOS 18 (Issues #185 and Blockstream/green_ios#70).

Our investigation has isolated a "Perfect Storm" of three compounding regressions that render generic ESP32 hardware unusable on iOS 18, starting from Firmware v1.0.31 and Green iOS v4.0.38.

1. Firmware Regression: The Upgrade (IDF 5.1 -> 5.4)

Diffing main/idf_component.yml between v1.0.30 and v1.0.34 reveals a massive jump in the underlying OS:

-    version: "==5.1.3"
+    version: "==5.4"

Jumping to ESP-IDF 5.4 brings a completely new NimBLE stack implementation and stricter PHY/Radio calibration timings. Generic hardware (like TTGO T-Display) often relies on "loose" tolerances that worked in IDF 5.1 but fail under the stricter requirements of IDF 5.4's BLE stack.

2. Firmware Logic: The Negotiation Blocker

Coinciding with the IDF upgrade, main/ble/ble.c was modified to explicitly reject the Central's connection parameters in favor of hardcoded values.

Code Diff (main/ble/ble.c):

+    case BLE_GAP_EVENT_CONN_UPDATE_REQ:
+        // The firmware now FORCES its own preferred intervals, overriding iOS suggestions
+        event->conn_update_req.self_params->itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN;
+        event->conn_update_req.self_params->itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX;
+        // ...
+        return 0;

By forcing self_params, the firmware prevents iOS from negotiating a stable interval. If the DIY board's crystal oscillator drifts slightly (common in generic hardware), this forced interval causes a supervision timeout.

Additionally, a "busy wait" was introduced in ble_writer, switching from portMAX_DELAY to polling every 100ms, introducing unnecessary CPU jitter during connection events.

3. Client-Side Trigger: GDK Overhead

On the iOS side (Green v4.0.38), Cargo.lock analysis shows GDK updated bitcoin to v0.32.4 (breaking change) and futures to v0.3.31. This heavier stack likely increased processing latency on the client side.

The Conclusion

The combination of IDF 5.4's strictness + Forced Parameters in ble.c + GDK Latency creates a scenario where generic hardware cannot reply fast enough or precisely enough for the iOS 18 Bluetooth Supervisor, resulting in immediate BLE_HS_ECONTROLLER (531) errors.

Proposed Fix:
To restore DIY compatibility without affecting official hardware:

  1. Critical: Remove or comment out the explicit parameter override block in BLE_GAP_EVENT_CONN_UPDATE_REQ within main/ble/ble.c. This reverts to the v1.0.30 behavior, allowing iOS to dictate the connection parameters.
  2. Alternatively: If these values are defined in main/ble/ble.h, they should be exposed via Kconfig (sdkconfig) so DIY builds can relax the timings without modifying source code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions