Skip to content

Latest commit

 

History

History
105 lines (53 loc) · 23.1 KB

mqtt-console.md

File metadata and controls

105 lines (53 loc) · 23.1 KB

MQTT Console lifecycle

Connect explicitly to a broker using mqtts:// or wss:// for certificate-bundle verification. Plain mqtt:// and ws:// require a second CONNECT tap within five seconds with unchanged connection settings. The default public test broker and device-specific topic are observable; use a disposable test message. SUBSCRIBE accepts one ASCII topic filter, and PUBLISH accepts one ASCII topic without wildcards. Topics/filters contain 1-127 bytes. QoS and retained-message controls apply to subsequent actions.

An explicit TLS/WSS profile stores the broker URI, username, password and topic in device settings. Developer builds do not encrypt these settings. Cleartext profiles stay in memory; deleting a saved profile requires two taps within five seconds. See the profile and metadata formats.

Broker settings and connection results

Broker URI input is limited to 255 bytes, including outside spaces/tabs. The raw limit is checked before trimming, so padding cannot hide a clipped endpoint. The pinned SDK URI parser checks host/port syntax; an explicit port must be 1-65535, and bracketed IPv6 hosts must pass address parsing. Scoped IPv6 URI literals are not supported. Embedded credentials and URI fragments are rejected. MQTT/MQTTS accepts a host and optional port/root slash; WS/WSS also accepts a path and query, passed unchanged to the SDK. The four scheme names are matched without regard to case.

Username and password each accept up to 64 UTF-8 bytes, both for Connect and Save Profile. The first excess character remains in the field and the raw byte count is checked before starting or saving. For example, 16 four-byte characters fit; 17 do not. A saved profile retains the accepted credential exactly. The URI field likewise retains a 256th ASCII byte so an overlong endpoint cannot silently connect to its prefix.

Editing the broker URI, username or password cancels a pending cleartext confirmation, including edits that restore the original text. The next CONNECT tap must show the warning again; a second tap within five seconds confirms the current settings. Focusing an input also cancels an earlier confirmation.

Re-opening the app reloads a valid saved TLS/WSS profile with its password masked. Connect stays explicit, and logging and retain default off. An invalid profile or a settings read error leaves the default form and reports the problem. DELETE PROFILE remains available when a key exists or its presence cannot be determined; confirmed absence disables it. You can explicitly replace an invalid profile by saving valid settings.

If saving or deleting reports not confirmed, the operation may already have changed device settings. The current form remains available; re-open to check what can be read, or use two fresh DELETE PROFILE taps to remove the key. Save, Connect/Disconnect, Clear, connection-field edits, form focus and expiry dismiss an earlier delete confirmation. DELETE PROFILE dismisses a pending cleartext connection confirmation, so the next Connect tap shows its warning again.

The connected status uses the accepted transport, even if the draft is later changed. MQTT/WS reports confirmed cleartext; MQTTS/WSS uses certificate verification. A transport/connection error immediately disables new Subscribe/Publish actions while the session remains owned until Disconnect/Home cleanup. The error survives the SDK's subsequent disconnect and ordinary acknowledgment statuses. Storage failures or omitted metadata/history warnings appear alongside it. Clear dismisses pending connection/profile-delete confirmation and clears history while retaining a connection error. Disconnect and a fresh connection reset the old error. A rejected subscription acknowledgment reports Broker rejected subscription, leaving the live connection usable.

Transport failures display every supplied nonzero ESP error, signed TLS stack code, certificate verification bitmask and socket errno. ESP error names include their hexadecimal code; TLS codes retain their sign, and verification flags use hexadecimal. Socket descriptions are bounded. A zero ESP error is omitted, so a socket failure is never described as ESP_OK. If all four fields are zero, the status says SDK supplied no error details. These codes report the SDK's diagnosis; an empty report does not identify a cause.

Subscribe and Publish

Each accepted tap captures its topic/filter, payload, QoS and retained state. The existing worker calls the SDK with that captured data, so editing the form or toggling QoS/retain while it waits affects the next action. The status and TX preview identify the captured topic. Subscribe and Publish remain unavailable while an action is pending or executing; their callbacks also reject stale disabled taps.

The SDK's Subscribe call writes to the network, and its Publish enqueue call takes a lock shared with the SDK network task. Both run on the worker so a network wait can finish while the UI remains responsive. The same worker processes Disconnect cleanup after releasing any action, and selects pending work and cleanup under one lock to preserve client ownership when notifications arrive together. Work still waiting for dispatch is discarded if the worker observes that stopping has begun. An action already inside the SDK can finish accepting a publish after Home; the old session's opted-in TX metadata then records that acceptance before cleanup ends.

Publish accepts an empty payload or up to 512 UTF-8 bytes. Excess input stays visible and is rejected before an action is queued. For example, 128 four-byte characters fit; 129 do not. A 128-byte ASCII topic is also retained and rejected instead of publishing to its 127-byte prefix. QoS 0 uses the SDK's explicit outbox storage option, so its successful message ID of zero is accepted. If the bounded outbox is full, the status asks you to wait before retrying. Queue acceptance is the TX outcome; broker delivery is established separately by the MQTT exchange.

A broker acknowledgment can arrive after the SDK releases its API lock but before that API returns to the worker. Action completion preserves any newer acknowledgment, subscription rejection, receive or disconnect status instead of replacing it with a queue/request message. An event revision is compared under the existing app lock; this does not delay the SDK callback or require another task.

Prepare a byte payload

In Byte Lab, prepare the Hex or encoded Number value and tap Copy bytes. In MQTT, PASTE BYTES captures those complete 0-128 bytes into a separate RAM draft and selects PAYLOAD: BYTES. Review the Hex view, topic, QoS and retain choice, then tap PUBLISH. Paste and changing payload mode never send, subscribe or write metadata. An empty byte payload is valid.

The payload button switches between the prepared bytes and PAYLOAD: TEXT, preserving the text field within the open screen. Text still accepts up to 512 UTF-8 bytes; its excess input remains visible and is rejected only when Text is the selected publish body. The bytes are prepared in Byte Lab rather than edited in MQTT. Their preview shows every original byte, including NUL and non-ASCII values.

Changing or clearing the shared clipboard does not replace an already prepared byte draft. Publishing captures the selected body's bytes and original topic/QoS/retain before dispatch; subsequent paste, mode or field edits cannot change that queued action. Prepared bytes and the selected mode survive Disconnect/Home in RAM, and the visible mode identifies which body the next explicit Publish uses. Clear clears history and the saved receive while preserving the prepared transmit draft. Restart clears prepared bytes. They are never stored in the connection profile or SD metadata.

Paste and mode switching dismiss pending cleartext/profile-delete confirmation without replacing connection or storage errors. They remain available offline or while an action waits, and are disabled throughout cleanup; stale clicks also revalidate that ownership. Byte publishes use the SDK's explicit byte length. Only after enqueue copies the bytes into its outbox does history convert them to a labeled ASCII preview.

Received messages and previews

The pinned SDK supplies the topic only on the first receive event. A header can fill its input buffer, leaving that event with zero payload bytes; the next event then also has offset zero and no topic. MQTT retains the original topic, timestamp, QoS, retain flag, message ID and total byte count while assembling contiguous data-only continuations. An empty payload completes on its topic-bearing event. History and metadata receive one entry only when the declared payload is complete.

Negative lengths/offsets, missing bytes or topic, inconsistent message fields, gaps, overlaps and repeated final fragments are rejected. A rejected continuation clears the assembly; it cannot finish later using stale bytes. Connection/error events also clear partial assembly. A fresh topic-bearing message can replace an unfinished one. A valid final fragment arriving before SDK shutdown finishes can still enter the old session's captured log queue after Home; unfinished messages produce no row.

Received payloads use a 512-byte ASCII preview. Printable ASCII, line feeds and tabs remain visible; carriage returns become line feeds and other bytes become .. The byte count describes the full received payload, including binary or larger messages. The RX history explicitly labels this representation as an ASCII preview and marks a capped payload.

Received topics use a 127-byte printable ASCII view. Longer topics are shortened, and non-printable/non-ASCII topic bytes become .. A changed topic is explicitly labeled topic preview in status/history, and its metadata outcome is preview_truncated, even when its payload fits. Such a topic field is a bounded representation and must not be treated as the complete original broker name. Topic controls/line breaks cannot create extra metadata lines. Ordinary printable ASCII names, including quotes, retain their normal CSV quoting.

Copy raw receive bytes

COPY RX shares the latest complete 0-128-byte receive with the RAM byte clipboard. Open Byte Lab and tap Paste hex to inspect exact binary bytes. Copy does not subscribe, publish, reconnect or write an SD row. The saved source label shows its captured topic, full byte count, QoS and retained flag; changing the draft topic or publishing leaves that receive intact. A shortened/transformed topic remains labeled topic preview, while the copied payload is exact.

An incomplete or rejected assembly cannot replace the last complete receive. A newer complete payload over 128 bytes disables copying and preserves the existing clipboard; it cannot be copied as a shortened prefix. An empty payload is a valid zero-byte copy. History-ring overflow does not lose the latest complete receive.

Disconnect/Home retains the receive in RAM independently of SDK buffers. Copy stays disabled while cleanup is busy, including during final fragments and error reporting; it is available after cleanup or re-entry. Starting a new session resets the saved receive. CLEAR clears the saved receive and queued/visible history, preserves accepted metadata and leaves the shared clipboard unchanged. Copy dismisses pending Connect/profile-delete confirmation, and its feedback does not replace connection or storage errors. The saved receive and clipboard disappear on restart.

Disconnect and Home

DISCONNECT posts the SDK's disconnect request and hands the session to the cleanup worker. Home also deletes the UI timer, clears the visible password, and detaches the widgets immediately. The worker completes any in-flight storage call, waits for SDK stop/destroy, writes already accepted pending metadata, and releases the session's buffers. MQTT remains busy throughout this work, including file close and storage-error reporting, so the existing OTA restart blocker cannot treat an unfinished cleanup as idle.

Re-opening MQTT while cleanup is running shows STOPPING. Connect, Subscribe, Publish, profile actions and the log toggle cannot start work during shutdown; their callbacks also check this state when an old click arrives. Connect, Save/Delete Profile and the log toggle still reject a disabled click after cleanup finishes but before the UI timer restores the controls. Delayed SDK connection/acknowledgment/error events cannot re-enable Subscribe or Publish or overwrite the shutdown status. A final received message can still join the bounded ring before SDK shutdown completes. The worker publishes its final status together with the idle state, preventing old cleanup from overwriting a new session's status.

Connect recreates required buffers if cleanup finished before the UI timer refreshed them. It also clears the previous session's pending status before starting a new client, so an older completion cannot replace the new connection status.

The preallocated worker remains asleep between actions and sessions. SDK action calls, shutdown and filesystem calls are cooperative operations; their configured network timeouts do not establish a strict whole-cleanup wall-clock bound. Home can detach the screen while the worker remains busy.

Pending SD metadata

SD logging starts OFF whenever MQTT opens and must be enabled explicitly. It records timestamp, TX/RX, topic, QoS, retained state, payload byte count and outcome. It never records credentials or payload contents. The log choice is sampled when a receive message completes or the SDK accepts a publish. Turning logging off stops future capture; metadata already accepted remains queued for writing.

The metadata queue holds eight pending entries plus one in-flight write and contains no payload preview. It is independent of the eight-entry UI message ring: history drops do not remove accepted metadata. If storage falls behind and its queue overflows, the oldest pending entries are dropped. The status reports omitted metadata while connected and again after Disconnect; the count persists until a new session. Both queues share one bounded PSRAM allocation.

The existing worker performs every filesystem operation, including directory creation, tail repair, append, flush, durable sync and close. The UI timer only updates history and controls. A pending Subscribe/Publish or shutdown takes precedence over the next queued write, although an in-flight filesystem call must return first. Clear affects visible history while accepted metadata remains owned by the worker.

Cleanup captures the old session's log choice before Home resets the new screen, allowing final SDK events to retain that choice through shutdown. It writes accepted entries serially and stops at the first storage failure. Each entry must complete flush, durable sync and close before it counts as successfully saved. A sync or close failure may leave bytes behind; the status therefore says the metadata log was not confirmed saved. Failure disables logging and discards remaining queued metadata for that session. Disconnect and start a new session before enabling logging again.

The captured storage-error callback runs once for the session's first error, on the worker after file close, while MQTT is still busy. During shutdown it runs after buffers are freed; during an active session it can run while the buffers remain allocated. Home can detach and re-enter while that callback waits. The callback must be thread-safe and must not call LVGL.

Software checks and remaining device checks

Run the focused MQTT checks to exercise the actual app, LVGL widgets and CSV helper with native task notifications and controlled SDK/file boundaries. Previous sources reproduce early busy release during blocked SD sync, SDK/filesystem calls on the UI thread and the lost topic after an empty first receive fragment. The 154 scenarios cover idle/stale actions, allocation/start errors, delayed events, SDK stop/destroy, active and shutdown file sync/close, Home/re-entry, pending completion, storage/profile errors, metadata privacy, logged and unlogged 25-session soaks and 50 repeated actions after warm-up. Controlled stalls verify captured inputs, bounded queues, Home/Disconnect ownership and cancellation before dispatch. Independent logging checks include capture before UI draining, history drops with complete CSV rows, metadata overflow, log-off/Clear during writes, first-error recovery and immediate reconnect after cleanup. Receive checks cover empty/fragmented/binary messages, 512/513/1024-byte payloads, topic views, malformed or interrupted continuations, duplicate completion, final fragments during Home and 50 repeated fragmented receives. Publish bounds include UTF-8 byte limits and 127/128-byte topics; QoS 0 checks the SDK's zero success ID and required storage option. Action controls also fit their row content bounds after explicit padding is applied.

Twenty-six connection scenarios additionally check all four transports, broker/credential bounds, confirmation expiry, SDK error retention, subscription rejection and concurrent metadata/history warnings. Six earlier connection regressions reproduce against the prior application source. Four changed-draft regressions cover synthetic broker/username/password fingerprint collisions and nine unfocused edit/restore sequences, including direct text replacement, typing/backspace and forward delete. The app handles LVGL value-change events to invalidate confirmation, and no longer uses a settings fingerprint. Six transport-diagnostic regressions reproduce misleading ESP_OK and missing TLS details in the preceding source. They cover individual and combined codes, an empty report and integer boundaries; the full diagnosis must survive disconnect, late acknowledgments and Clear until explicit cleanup. URI checks compile the pinned SDK parser and use the native platform's IPv6 address parser.

Twenty-four profile scenarios retain an NVS model across app entries. They check exact masked 64-byte credential reload, overwrite/delete, thirteen malformed blobs, wrong key type, read failures and a size change between reads, failures before/after modeled write effects, confirmation handoffs and stale disabled taps. Twenty-five reload/delete/save cycles recover the same native memory, allocation and handle counts after matching warm-up cycles. Seven profile regressions reproduce against the previous source.

Thirteen packet cases run the actual pinned SDK MQTT 3.1.1 codec against independently authored bytes. Captured Subscribe/Publish inputs encode exact topic, UTF-8 payload, QoS/retain and empty-payload packets; a separate CONNECT vector checks framing. Literal and larger PUBLISH fixtures use the real SDK decoders before modeled event dispatch. These cover empty/binary bodies, duplicate flags, a topic that fills the first input buffer, fragmented/capped bodies, Home completion/abandonment, all 256 fixed-header values and eight remaining-length boundaries. Deliberate codec corruptions fail the checks, and the decoded empty-first-fragment case reproduces the archived lost-topic regression. See packet test scope.

The 154-case app/codec suite's client APIs and event dispatch remain modeled. The client/broker suite also runs the unmodified SDK client read/dispatch loop, packet codec, outbox and transport dispatch over real loopback sockets. Sixteen standalone cases verify CONNECT/SUBACK, retained and empty publishes, binary and fragmented receives, the empty-first-fragment sequence, both directions of QoS acknowledgment exchanges, malformed/incomplete packets and 25 combined sessions with stable resources.

Thirty-one further cases compile the actual console, LVGL and CSV helper with that SDK client. Real taps select QoS/retain/logging, confirm cleartext, subscribe and publish. Checks compare visible previews and metadata with actual broker exchanges, including the topic-filled first buffer, empty/binary/capped receives, connection/subscription refusal and Home during a blocked receive. Complete final data is logged once after Home; abandoned data produces no row. Four early-ACK cases hold the worker's API return until the real SDK dispatches the broker reply, reproducing lost subscription success/refusal and QoS 1/2 publish acknowledgment statuses against the previous source. Twenty-five app sessions recover the same LVGL memory and native handles, with only the standby worker retained between sessions.

Four byte-publish cases use the actual Byte Lab Copy/Paste controls around the complete SDK/broker exchange. They publish 128 raw bytes at QoS 0/1/2, plus an empty payload, after replacing the shared clipboard without repasting. Exact wire bytes, complete receive copying, Byte Lab Hex results and metadata-only rows must match.

Nine of these cases hold native sync/close calls or inject a one-shot open/flush/sync/close failure while actual broker traffic continues. They check prompt Home/re-entry, cancellation of a pending publish, continued SDK acknowledgments and exact metadata overflow order/counts. A held TX write plus twenty QoS 1 receives and a marker yields nine CSV rows and 13 reported omissions. Error callbacks run once after file close while MQTT is busy; held callbacks preserve ownership through Home, including after shutdown releases the client and buffers. Failed sessions reject logging taps and later metadata writes, then reconnect and write complete metadata successfully. Flush/sync/close faults deliberately allow native write effects before reporting EIO, verifying that bytes left on disk do not imply confirmed persistence. These cases adapt file boundaries and do not establish physical SD behavior.

Native thread/lock/event services are adapted; explicit joins before client destruction strengthen the SDK task lifetime and do not establish device stop/destroy timing. The integration cases use native files and an absent-profile NVS boundary; profile scenarios retain a RAM model within one process. DNS, TLS/WSS, flash NVS, cold reboot, power loss, physical credential erasure, real Wi-Fi/SD timing and faults, all three QoS modes on hardware and both panels remain unverified. Historical ST7121 broker/retained-message/Home checks remain recorded in the hardware checklist.

The complete-client app soak accounts for measured padding in LVGL's retained display event array when comparing free bytes. It separately records raw free space and requires unchanged allocation counts, event counts and handles. A pool trace reproduced the 24-byte padding variation with all other retained blocks unchanged; this prevents renderer bookkeeping from being reported as a console leak.

The Windows CI job runs all MQTT cases with pinned source inputs from the firmware build and blocks release publication on failure. The local clean rehearsal covers 154 app/codec cases, 16 standalone client cases, 31 complete-client console cases and offline/SPI checks using only the source bundle. The 2026-10-06 hosted run passed the firmware and Windows jobs; downloaded artifacts verify the source handoff and all 201 MQTT cases. The workflow retains logs, byte transcripts, source metadata and console CSVs on failure. A failing Windows artifact upload and the tag-release failure gate still need hosted verification.