mqtt.library API reference¶
This page is the same reference shipped as mqtt.doc in the release archive's developer/ directory, generated here so it also shows up in the web docs. See mqtt.library for the narrative guide (installing it, the threading model, auto-reconnect) - this page is the function-by-function reference.
Background¶
Name¶
mqtt.library -- MQTT 3.1.1 client for classic AmigaOS (V1)
Function¶
mqtt.library gives any AmigaOS program a native MQTT 3.1.1 client: connect to a broker, publish, subscribe, and receive messages, without linking any protocol code of your own. It is the shared library counterpart to the mqtt_pub/mqtt_sub command-line tools that ship alongside it - all three sit on top of the same portable MQTT codec and client state machine.
Architecture¶
Every APTR client handle returned by MQTT_CreateClient() is backed by its own dedicated AmigaOS subprocess, spawned by MQTT_CreateClient() and torn down by MQTT_DeleteClient(). That subprocess owns the bsdsocket.library connection and drives the MQTT client state machine; the calling task never touches the socket directly. This exists because bsdsocket.library handles are per-task - a shared library has no task of its own to own one in, so each client gets a small task-equivalent that can.
The caller's task and its client's subprocess talk over two Exec message ports:
- A CALLER-owned delivery port, on which the subprocess PutMsg()s one struct MqttMessage per incoming PUBLISH. MQTT_GetMessage() simply GetMsg()s this port.
- A CHILD-owned command port, on which the caller PutMsg()s a request (connect/publish/subscribe/disconnect) and Wait()s for the reply. Every MQTT_* call that talks to the subprocess this way (MQTT_Connect, MQTT_Publish, MQTT_Subscribe, MQTT_Disconnect) is therefore synchronous from the caller's point of view: it blocks until the subprocess has finished that step (including, for a QoS 1 publish, waiting for the broker's acknowledgement) and returns its result.
Threading Model¶
A client handle is owned by exactly one caller task - the one that called MQTT_CreateClient(). Every other call on that handle, including MQTT_GetMessage(), MQTT_FreeMessage(), and MQTT_DeleteClient(), must be made from that same task: the handle's delivery message port is a caller-task-owned Exec resource, and Exec message ports are not safe to share across tasks. A program that wants MQTT traffic on more than one task needs one client handle (one MQTT_CreateClient() call) per task.
QoS Support¶
QoS 0 and QoS 1 only. QoS 2 is deliberately out of scope for this library, exactly as it is for mqtt_pub/mqtt_sub (see the project's docs/PROTOCOL.md) - MQTT_Publish() rejects a qos argument greater than 1.
- QoS 0 publish: fire-and-forget. MQTT_Publish() returns as soon as the PUBLISH packet has been written to the transport; no broker acknowledgement is waited for.
- QoS 1 publish: MQTT_Publish() returns only once the broker's PUBACK has arrived, retransmitting (DUP flag set) roughly every 5 seconds, up to 3 retransmits (4 sends total, ~15-20 seconds worst case) before giving up. Because each client handle's commands are synchronous, at most one QoS 1 publish is ever outstanding per handle.
- Subscribe: MQTT_Subscribe() returns only once the broker's SUBACK has granted the subscription (or refused it, or ~10 seconds have passed with no SUBACK at all).
- Incoming messages of either QoS are delivered identically via MQTT_GetMessage() - see struct MqttMessage's mm_Qos field for the QoS the broker actually delivered at (MQTT delivery QoS is min(subscribe QoS, publish QoS), same as the wire protocol).
Auto-Reconnect (mco_AutoReconnect)¶
struct MqttConnectOpts's mco_AutoReconnect field, passed to MQTT_CreateClient(), controls what happens after an unexpected connection drop (a transport error, or a keepalive timeout) once MQTT_Connect() has already succeeded once:
- FALSE (the default for a zeroed struct): today's plain behaviour. The client stays disconnected until the caller makes a fresh, explicit MQTT_Connect() call.
- TRUE: the client's own subprocess re-establishes the connection by itself, with exponential backoff (1s, 2s, 4s, ... capped at 32 seconds, retried forever), and automatically re-issues every MQTT_Subscribe() filter made since the last MQTT_Connect() on each successful reconnect. An explicit MQTT_Disconnect() or a failed initial MQTT_Connect() does NOT trigger this - only a drop after a successful connect does.
While a reconnect is in progress, MQTT_Publish() and MQTT_Subscribe() both fail fast with MQTTERR_NOTCONNECTED (nothing is queued for later delivery), and a second MQTT_Connect() call made mid-reconnect also fails fast the same way, rather than racing the background attempt. MQTT_GetMessage() is unaffected: it keeps returning any messages already queued, and any a successful reconnect goes on to deliver - it never itself fails. MQTT_Disconnect() and MQTT_DeleteClient() both cancel an in-progress reconnect immediately.
Error Codes¶
Every MQTT_* function that returns a status returns 0
(MQTTERR_OK) on success and a negative code on failure. Two
families of negative codes are possible:
- Codes originating in the library's own subprocess/IPC
plumbing, defined in <libraries/mqtt.h>:
MQTTERR_OK 0 Success.
MQTTERR_NOMEM -200 AllocVec()/CreateMsgPort()/process
spawn failed for lack of memory.
MQTTERR_NOSTACK -201 Reserved: MQTT_CreateClient() has
no error-code return channel (it
returns APTR, bare NULL on every
failure, including a
CreateNewProcTags() failure) -
this code is never actually
returned today.
MQTTERR_NOTCONNECTED -202 Called before MQTT_Connect() has
succeeded, after
MQTT_Disconnect(), or while
mco_AutoReconnect is mid-
reconnect (see above).
MQTTERR_TIMEOUT -203 MQTT_Publish() at QoS 1: no
PUBACK within the retry budget.
MQTT_Subscribe(): no SUBACK
within ~10 seconds.
MQTTERR_REFUSED -204 MQTT_Subscribe(): the broker's
SUBACK refused the subscription
(return code 0x80).
MQTTERR_STATE -205 MQTT_Connect() called on a handle
that is already connected.
- Codes passed straight through from the portable core
(src/core/mqtt_packet.h's mqtt_err and src/core/mqtt_client.h's
mqtt_client_err, both returned as negative values here) when
the failure originates below the library's own plumbing - a
malformed packet, a rejected CONNECT, a transport error, and
so on. See those two headers in the project's source for the
exact list; from a caller's point of view the only thing that
matters is that any negative return is a failure and 0 is
success.
See Also¶
MQTT_CreateClient, MQTT_Connect, MQTT_Publish, MQTT_Subscribe, MQTT_GetMessage, MQTT_Disconnect, MQTT_DeleteClient
MQTT_CreateClient¶
Name¶
MQTT_CreateClient -- create a new MQTT client handle (V1)
Synopsis¶
client = MQTT_CreateClient(host, port, opts)
D0 A0 D0 A1
APTR MQTT_CreateClient(STRPTR, UWORD, struct MqttConnectOpts *);
Function¶
Allocates a new MQTT client handle and spawns the dedicated AmigaOS subprocess that will own its bsdsocket.library connection (see mqtt.library/--background--). The handle is not yet connected to anything - call MQTT_Connect() next.
Every string field of opts (mco_ClientID, mco_Username,
mco_Password) is deep-copied before this call returns, so opts
and everything it points at may be freed or reused by the caller
immediately afterwards.
Inputs¶
host - broker hostname or dotted-decimal IP address,
NUL-terminated. Must not be NULL.
port - broker TCP port (1883 is the conventional unencrypted MQTT
port).
opts - connect options (client id, credentials, keepalive,
clean-session, mco_AutoReconnect, mco_TLS,
mco_TLSInsecure, mco_CAFile); see struct MqttConnectOpts
in
Result¶
client - a new client handle, or NULL if the handle or its subprocess could not be created (out of memory, or CreateNewProcTags() failed). The returned handle belongs to the calling task - see mqtt.library/--background--'s THREADING MODEL section.
Notes¶
Creating a client does not touch the network - no socket is opened until MQTT_Connect() is called.
mco_TLS connects via AmiSSL instead of a plain TCP transport, if this build of mqtt.library was linked with AmiSSL support (see the Makefile's M68K_HAS_AMISSL) - otherwise MQTT_Connect() fails with MQTTERR_NOTCONNECTED, the same as any other missing capability. AmiSSL is CPU-intensive: a genuinely stock, unaccelerated 68020 has been found to intermittently fail under it (see userdocs/CLI-Reference.md's "A note on TLS and CPU speed"). mco_TLSInsecure skips certificate/hostname verification and is ignored unless mco_TLS is also set - for testing against self-signed or otherwise untrusted brokers only, never for production use.
mco_CAFile (issue #13) additionally trusts a PEM file's CA alongside AmiSSL's bundled trust store - for a broker behind a private CA that isn't in it. Certificate verification checks the broker cert's validity dates against the Amiga's own system clock, same as any TLS client - a system clock that's wrong (common on real hardware with a dead or unset battery-backed RTC) will make a perfectly good certificate look not-yet-valid or expired and fail the handshake. Set the clock (SetClock, IControl, or NTP via a suitable client) before relying on certificate verification.
See Also¶
MQTT_Connect, MQTT_DeleteClient
MQTT_DeleteClient¶
Name¶
MQTT_DeleteClient -- destroy a client handle and free its resources (V1)
Synopsis¶
MQTT_DeleteClient(client)
A0
VOID MQTT_DeleteClient(APTR);
Function¶
Disconnects client if it is still connected (equivalent to
calling MQTT_Disconnect() first), cancels any in-progress
mco_AutoReconnect reconnect attempt, terminates its subprocess,
and frees every resource associated with the handle, including
any undelivered messages still queued on its delivery port. Any
struct MqttMessage the caller already obtained via
MQTT_GetMessage() and has not yet freed remains the caller's
responsibility to free with MQTT_FreeMessage() - MQTT_DeleteClient()
only frees messages still queued, not ones already handed out.
After this call, client must not be used again.
Inputs¶
client - a handle from MQTT_CreateClient(). NULL is accepted and does nothing.
Result¶
None.
Notes¶
Must be called from the same task that created the handle - see mqtt.library/--background--'s THREADING MODEL section. This call blocks until the client's subprocess has actually finished terminating.
See Also¶
MQTT_CreateClient, MQTT_Disconnect
MQTT_Connect¶
Name¶
MQTT_Connect -- open the TCP connection and perform the MQTT CONNECT/CONNACK handshake (V1)
Synopsis¶
result = MQTT_Connect(client)
D0 A0
LONG MQTT_Connect(APTR);
Function¶
Opens a bsdsocket.library TCP connection to the host/port given to MQTT_CreateClient(), sends an MQTT CONNECT packet built from the connect options given at that time, and waits for the broker's CONNACK. Returns once the client is fully connected or the attempt has definitively failed.
Calling MQTT_Connect() again while mco_AutoReconnect is already reconnecting in the background fails fast with MQTTERR_NOTCONNECTED, rather than racing that attempt - see mqtt.library/--background--'s AUTO-RECONNECT section.
Calling MQTT_Connect() again on a handle that is already connected fails fast with MQTTERR_STATE and leaves the existing connection untouched - call MQTT_Disconnect() first if a fresh connection is wanted.
Inputs¶
client - a handle from MQTT_CreateClient().
Result¶
result - 0 (MQTTERR_OK) on success; a negative error code on failure. See mqtt.library/--background--'s ERROR CODES section.
Notes¶
Once this call has succeeded, if mco_AutoReconnect was set at MQTT_CreateClient() time, any later unexpected drop is handled automatically by the client's own subprocess - there is no need to call MQTT_Connect() again after such a drop.
See Also¶
MQTT_CreateClient, MQTT_Disconnect
MQTT_Publish¶
Name¶
MQTT_Publish -- publish a message to a topic (V1)
Synopsis¶
result = MQTT_Publish(client, topic, payload, len, retain, qos)
D0 A0 A1 A2 D0 D1 D2
LONG MQTT_Publish(APTR, STRPTR, APTR, ULONG, LONG, UBYTE);
Function¶
Publishes len bytes at payload to topic, at the requested
QoS. At QoS 0 this returns as soon as the packet is written to
the transport; at QoS 1 it returns only once the broker's PUBACK
has arrived (retransmitting a few times first if needed). See
mqtt.library/--background--'s QOS SUPPORT section for the exact
timing and retry budget.
Inputs¶
client - a handle from MQTT_CreateClient(), already connected
via MQTT_Connect().
topic - the topic to publish to, NUL-terminated. Must not be
NULL.
payload - the message body. May be any bytes (not required to be
text); may be NULL only if len is 0.
len - number of bytes at payload.
retain - non-zero to ask the broker to retain this message as
the topic's last-known value for future subscribers;
zero for a normal (non-retained) publish.
qos - 0 or 1. Any other value is rejected. QoS 2 is out of
scope for this library (see docs/PROTOCOL.md).
Result¶
result - 0 (MQTTERR_OK) on success; a negative error code on failure (including MQTTERR_TIMEOUT if a QoS 1 publish's PUBACK never arrived, or MQTTERR_NOTCONNECTED if the client is not currently connected, including while mco_AutoReconnect is mid-reconnect). See mqtt.library/--background--'s ERROR CODES section.
Notes¶
Because each client handle's commands are synchronous, at most one QoS 1 publish is ever outstanding per handle - a second MQTT_Publish() call cannot be made until the first has returned. A program that needs concurrent publishes should use more than one client handle (each on its own task; see the THREADING MODEL section).
See Also¶
MQTT_Connect, MQTT_Subscribe
MQTT_Subscribe¶
Name¶
MQTT_Subscribe -- subscribe to a topic filter (V1)
Synopsis¶
result = MQTT_Subscribe(client, filter, qos)
D0 A0 A1 D0
LONG MQTT_Subscribe(APTR, STRPTR, UBYTE);
Function¶
Sends an MQTT SUBSCRIBE for filter at the requested QoS and
waits for the broker's SUBACK. Once this returns 0, messages
published to any topic matching filter (which may use the MQTT
wildcards + and #) start arriving via MQTT_GetMessage().
If mco_AutoReconnect was set at MQTT_CreateClient() time, this filter is remembered and automatically re-subscribed after any future reconnect - no need to call MQTT_Subscribe() again after a drop.
Inputs¶
client - a handle from MQTT_CreateClient(), already connected via
MQTT_Connect().
filter - the topic filter to subscribe to, NUL-terminated (MQTT
wildcards +/# allowed). Must not be NULL.
qos - the maximum QoS requested for this subscription (0 or
1). The broker may grant a lower QoS than requested;
delivered messages report the QoS actually used in
struct MqttMessage's mm_Qos field.
Result¶
result - 0 (MQTTERR_OK) on success; MQTTERR_REFUSED if the broker refused the subscription (SUBACK return code 0x80); MQTTERR_TIMEOUT if no SUBACK arrived within ~10 seconds; MQTTERR_NOTCONNECTED if the client is not currently connected. See mqtt.library/--background--'s ERROR CODES section.
Notes¶
A single client handle may hold more than one active subscription - call MQTT_Subscribe() once per filter.
See Also¶
MQTT_Connect, MQTT_Publish, MQTT_GetMessage
MQTT_GetMessage¶
Name¶
MQTT_GetMessage -- retrieve the next queued incoming message (V1)
Synopsis¶
msg = MQTT_GetMessage(client)
D0 A0
struct MqttMessage *MQTT_GetMessage(APTR);
Function¶
Returns the next message delivered on any of this client's active subscriptions, in FIFO order, or NULL if none is currently queued. This call never blocks - it is a poll, meant to be called periodically (e.g. in a loop with a short Delay() between calls, checking for Ctrl-C between polls) rather than waited on.
Inputs¶
client - a handle from MQTT_CreateClient().
Result¶
msg - a pointer to a library-allocated struct MqttMessage (see
Notes¶
Every non-NULL result must eventually be passed to MQTT_FreeMessage() - never FreeVec() it directly, and never ReplyMsg() its embedded mm_Msg (it is unused). This call always succeeds in the sense of never failing outright, including while mco_AutoReconnect is mid-reconnect - it simply returns NULL more often until the connection (and message flow) is restored.
See Also¶
MQTT_Subscribe, MQTT_FreeMessage
MQTT_FreeMessage¶
Name¶
MQTT_FreeMessage -- free a message returned by MQTT_GetMessage() (V1)
Synopsis¶
MQTT_FreeMessage(client, msg)
A0 A1
VOID MQTT_FreeMessage(APTR, struct MqttMessage *);
Function¶
Frees a struct MqttMessage previously returned by MQTT_GetMessage(), including its topic and payload storage (all one allocation - the library's internal layout is not part of the API, so never FreeVec() a message directly).
Inputs¶
client - the handle the message came from. (Reserved for future use; the current implementation does not need it to free the message, but always pass the correct handle.) msg - a message from MQTT_GetMessage(). NULL is accepted and does nothing.
Result¶
None.
See Also¶
MQTT_GetMessage
MQTT_Disconnect¶
Name¶
MQTT_Disconnect -- close the connection to the broker (V1)
Synopsis¶
MQTT_Disconnect(client)
A0
VOID MQTT_Disconnect(APTR);
Function¶
Sends an MQTT DISCONNECT and closes the underlying transport. Also cancels an in-progress mco_AutoReconnect reconnect attempt, if one is running, and forgets the client's remembered subscription list (so a later MQTT_Connect() starts clean - any subscriptions needed again must be re-issued with MQTT_Subscribe()).
Unlike MQTT_Connect()/MQTT_Publish()/MQTT_Subscribe(), this function has no return value: there is nothing meaningful for the caller to do differently if the DISCONNECT packet itself could not be sent, since the transport is being torn down either way.
Inputs¶
client - a handle from MQTT_CreateClient(). If not currently connected, this call does nothing.
Result¶
None.
Notes¶
The client handle itself remains valid after this call - call MQTT_Connect() again to reconnect, or MQTT_DeleteClient() to destroy the handle entirely.
See Also¶
MQTT_Connect, MQTT_DeleteClient