Segment pool entries are 20 bytes on this build, not the nominal 16 that
struct tcp_seg suggests; the comment now quotes the measured figure.
The MEM_SIZE justification did not close as written: two connections at
~6KB each is ~12KB, which fits inside the old 16KB heap. The missing
term is that mem.c is first-fit, so what has to be free is a contiguous
1.5KB block, and at 75% occupancy interleaved with ARP, DHCP/DNS and
mDNS allocations the largest run collapses long before the total does.
That is also why the failure looked intermittent. Spelled out in both
the Python comment and the header template.
Hoist the lwIP sizing values to module constants so the two numeric
invariants can be tested: the segment pool must stay above the per-PCB
send queue, and MEM_SIZE must stay under 64000 or lwIP widens
mem_size_t to u32_t. Both were previously enforced by prose alone, and
the first would silently rebuild the starvation this change removes.
The generated header is byte identical.
Two lwIP values on rp2 were copied from ESP32, where MEM_LIBC_MALLOC=1
and MEMP_MEM_MALLOC=1 make them labels rather than caps. On rp2 they
are static pools and a fixed heap, so they became hard limits.
MEMP_NUM_TCP_SEG is a global pool but was set to TCP_SND_QUEUELEN (17),
which is per-PCB. lwIP's sanity check only demands >=, so that is the
floor for a single connection: one busy PCB could drain the pool for
every other PCB, including the other API connections, mDNS and
web_server. Size it to 2x the per-PCB queue instead, which costs about
340 bytes.
MEM_SIZE stayed at arduino-pico's 16KB. TCP_OVERSIZE defaults to
TCP_MSS, so one PCB holding a full TCP_SND_BUF pins about 6KB; two
connections exhausted the heap while api's max_connections on rp2 is 4.
Raise it to 32KB, which is arduino-pico's own next tier.
Either limit surfaces the same way: ERR_MEM from tcp_write(), then
EWOULDBLOCK, then a refused send_message().
The docstring now records why MEMP_MEM_MALLOC=1 is not the answer here,
since it looks like the obvious fix and is not. lwIP's heap takes its
protection from LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT, which defaults
to 0, so under NO_SYS=1 mem_malloc() and mem_free() are unprotected,
and memp.c calls mem_malloc() outside SYS_ARCH_PROTECT. Routing RX pbuf
allocation through it lets the pendsv IRQ race the main loop on one
unguarded free list; that faults within seconds on CYW43.
Also fixes two stale comments in api_frame_helper.h that still claimed
rp2 uses an 8xMSS send buffer; it has been 4xMSS since #14843.
RAM cost on tests/components/bluetooth_proxy/test.rp2040-ard.yaml is
16,724 bytes, leaving about 162KB for the heap.