fix(compile): answer the Bluetooth error ourselves instead of pointing at an IDE menu

A sketch that includes BTstackLib on a CYW43 board fails with arduino-pico's
static_assert: "This library needs Bluetooth enabled. Use the 'Tools->IP/
Bluetooth Stack' menu in the IDE to enable it." There is no such menu here, so
it reads as a setting we forgot to expose. What is actually true is simpler:
the CYW43439's Bluetooth controller is not emulated — only its WiFi side is —
so there is nothing to switch on.

Appends that, leaving the compiler's own diagnostic intact above it because the
developer still needs the real text. Deliberately a separate helper from
humanize_cli_error, which REPLACES arduino-cli's own failures; this one only
adds context under a compiler message.

Worth knowing for whoever picks up the BT work: this is the honest-failure
milestone. Making a BT sketch compile at all means flipping this board's FQBN
to ipbtstack=ipv4btcble, which swaps liblwip.a for liblwip-bt.a and costs every
sketch on the board ~71 KB flash and ~18 KB RAM — and leaves it reaching
HCI_STATE_WORKING while advertising to nobody. The mechanism is mapped in
project/pimoroni-pico-plus-2w/STATUS.md.
This commit is contained in:
David Montero Crespo 2026-08-22 04:38:31 +02:00
parent 89025212ca
commit cdc72fd4fa
2 changed files with 73 additions and 1 deletions

View File

@ -55,6 +55,41 @@ _NETWORK_MARKERS = (
)
# ── Compiler diagnostics that need a Velxio answer, not the IDE's ────────────
# arduino-pico gates its Bluetooth libraries behind a static_assert whose text
# tells the user to flip "Tools->IP/Bluetooth Stack" — a menu that exists in the
# Arduino IDE and not here. Left alone it reads like a setting we forgot to
# expose. The truth is simpler and worth saying plainly: the CYW43439/RM2's
# Bluetooth side is not emulated, only its WiFi side is, so there is nothing to
# enable.
_BT_ASSERT_MARKERS = ("_needsbt.h", "This library needs Bluetooth enabled")
_BT_NOT_EMULATED_NOTE = (
"\n"
"Velxio: Bluetooth is not emulated on this board. The CYW43439 (the RM2\n"
"module on the Pimoroni Pico Plus 2 W, and the same chip on the Pico W)\n"
"is emulated for WiFi only — association, DHCP, DNS and real HTTP all\n"
"work — but its Bluetooth controller is not modelled, so there is no\n"
"'IP/Bluetooth Stack' setting to turn on here. Sketches that use WiFi.h,\n"
"HTTPClient or WiFiServer compile and run.\n"
)
def annotate_build_stderr(stderr: str | None) -> str | None:
"""Append a Velxio-authored note to compiler output we know is misleading.
Kept separate from humanize_cli_error: that one rewrites arduino-cli's own
failures, this one leaves the compiler's diagnostics intact and adds context
underneath, because the developer still needs the real error text.
"""
if not stderr:
return stderr
if any(marker in stderr for marker in _BT_ASSERT_MARKERS):
return stderr.rstrip() + "\n" + _BT_NOT_EMULATED_NOTE
return stderr
def humanize_cli_error(raw: str | None, *, action: str = "run arduino-cli") -> str:
"""One readable sentence for an arduino-cli failure.
@ -688,7 +723,7 @@ class ArduinoCLIService:
"success": False,
"error": "Compilation failed",
"stdout": result.stdout,
"stderr": result.stderr
"stderr": annotate_build_stderr(result.stderr),
}
except Exception as e:

View File

@ -0,0 +1,37 @@
"""
The Bluetooth compile error says something Velxio can answer better.
arduino-pico gates its BT libraries behind a static_assert telling the user to
use the Arduino IDE's "Tools->IP/Bluetooth Stack" menu. In Velxio that menu does
not exist, so the message reads like a setting we forgot to expose rather than
what it is: the CYW43439's Bluetooth side is not emulated at all.
"""
from app.services.arduino_cli import annotate_build_stderr
REAL_ERROR = (
"/root/.arduino15/packages/rp2040/hardware/rp2040/6.0.0/cores/rp2040/_needsbt.h:4:24: "
"error: static assertion failed: This library needs Bluetooth enabled. "
"Use the 'Tools->IP/Bluetooth Stack' menu in the IDE to enable it\n"
"Error during build: exit status 1\n"
)
def test_the_bluetooth_assert_gets_a_velxio_answer():
out = annotate_build_stderr(REAL_ERROR)
# The compiler's own text survives — the developer still needs it.
assert '_needsbt.h' in out
assert 'static assertion failed' in out
# ...and we say what is actually true underneath.
assert 'Bluetooth is not emulated on this board' in out
assert 'WiFi' in out
def test_an_ordinary_error_is_left_alone():
ordinary = "sketch.ino:5:3: error: 'foo' was not declared in this scope\n"
assert annotate_build_stderr(ordinary) == ordinary
def test_empty_output_is_passed_through():
assert annotate_build_stderr(None) is None
assert annotate_build_stderr('') == ''