Files
MeshCore/src/helpers/bridges
agessaman 3afd7ebedb fix(mqtt): write every owned field on every apply; recreate only where needed (F02)
The stale-configuration fix, and the reason the four preceding commits went
first.

`setupSlot()` applied configuration field by field, interleaved with the
decisions that produced it, and cleared stale fields only when
`slot.initial_connect_done` was set — which `teardownSlot()` clears at the top
of every reconfigure. So live reconfiguration always skipped the cleanup. On a
5-slot board this was reproducible in one command: moving a slot from a JWT
preset to an anonymous custom endpoint sent the previous configuration's
`v1_<pubkey>` username in the CONNECT to the new broker (95-byte CONNECT
captured at a throwaway broker on the LAN). The same config after a reboot sent
26 bytes with no username, which is what pinned it to the reconfigure path.

That cleanup could not have worked anyway: it nulled the wrapper's pointers, and
IDF's `esp_mqtt_set_if_config()` treats NULL as "leave unchanged", so an
SDK-held credential survives it. An empty string does overwrite it, and leaves
the CONNECT's username flag clear.

So configuration is now decided in full first, then applied in one place:

- every owned field is written on every apply — server, trust material,
  username, password — with absent credentials written as "" rather than
  omitted;
- `mqttConfigRecreateDecision()` (previous commit) picks reuse or recreation.
  Credentials, auth-mode changes and endpoint moves within one scheme reuse the
  client, because a create/destroy cycle on the reconnect and renewal paths is
  the fork's documented internal-heap fragmentation driver. A transport
  (scheme) change, a trust-policy or CA change, and buffer-capacity growth
  recreate it, because those are the fields a write cannot safely replace;
- `recreateSlotClient()` will not destroy a client whose stop was not proven —
  it quarantines it and fails the setup instead;
- the applied configuration is recorded only after the client actually starts.

Verified on hardware (Heltec V4, 5 live slots, both paths):
- reuse path — a custom JWT slot (639-byte CONNECT carrying `v1_<pubkey>` plus
  the token) with its audience cleared now sends 26 bytes with no credentials,
  with no client recreation;
- recreate path — `mqtt://` to `wss://` logged `recreating client
  (transport-change)` and reconnected.

Also fixes a cap regression the hardware run caught: `canActivateSlot()` now
counts the positions held by the *other* slots. A slot being reconfigured keeps
its esp-mqtt task alive, so once the count included live clients it counted
itself out of its own position and a full board refused to reconfigure a slot —
disabling it instead. Asking "is there room for this slot" rather than "is
there room for one more" is also what makes a resource-based count safe.
2026-09-09 18:03:59 -07:00
..
2025-12-02 10:31:24 +00:00