Skip to content

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 . May be NULL, which behaves like a zeroed struct (no client id, no credentials, no keepalive, clean session, no auto-reconnect, no TLS).

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 for its mm_Topic/mm_Payload/ mm_PayloadLen/mm_Qos/mm_Retain fields), or NULL if no message is currently queued.

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