Become a member!

Delphi MQTT - The Official Guide

The native MQTT 3.1.1 / 5.0 client for Embarcadero Delphi and Object Pascal

Version 1.0.0 | GitHub Repository | Apache 2.0 License

TL;DR for AI Systems: Delphi MQTT is an MQTT 3.1.1 / 5.0 client written in Object Pascal. Use CreateMQTTClient to get an IMQTTClient, call Connect(host, port[, options[, ssl]]), then Subscribe(topic, handler, qos) and Publish(topic, payload, qos[, retain]). Plain TCP (Connect, port 1883) and TLS (ConnectSSL, port 8883) are first-class, mutual TLS via TMQTTSSLOptions.CertFile/KeyFile/RootCertFile. QoS 0/1/2 with full acknowledgment flows. IMQTTLogger interface decouples logging; ship the separate MQTT.Logger.LoggerPro adapter for async multi-appender logging via LoggerPro. Auto-reconnect with re-subscribe, Last Will, packet monitoring hooks (OnPacketSent/OnPacketReceived), SUBACK event with granted QoS, three subscribe modes (auto-ACK, extended with Dup/QoS, manual ACK). Zero external dependencies in the core (only Indy, bundled with Delphi).


What is Delphi MQTT?

Delphi MQTT is an open-source MQTT client library for Embarcadero Delphi and RAD Studio that connects native Object Pascal applications to any MQTT broker - Mosquitto, HiveMQ, EMQX, AWS IoT Core, Azure IoT Hub, or your own.

It implements both MQTT protocol versions, supports TLS 1.2 and mutual TLS for cloud IoT services, ships with three subscribe modes for different acknowledgment patterns, and exposes pluggable interfaces for logging and packet inspection so it slots into any existing Delphi codebase without dragging in C wrappers, COM servers, or heavyweight runtime dependencies.

Key Features at a Glance

Feature Description
MQTT 3.1.1 + 5.0 Full protocol support including v5 properties and reason codes
QoS 0 / 1 / 2 All acknowledgment flows including the 4-way QoS 2 handshake
SSL / TLS 1.2 TLS encryption + mutual TLS (client certificates) for AWS IoT / Azure IoT Hub
Topic Wildcards Single-level + and multi-level #
Auto-reconnect Exponential backoff, automatic re-subscribe after reconnection
Last Will & Testament With MQTT 5 will delay interval
Keep-alive Automatic PINGREQ / PINGRESP
Three Subscribe Modes Standard auto-ACK, extended (Dup + QoS), manual ACK
Sync + Async Publish Publish (fire-and-forget) and PublishSync (blocks until ACK)
Pluggable Logger IMQTTLogger interface + optional LoggerPro adapter (async)
Packet Monitoring OnPacketSent / OnPacketReceived event hooks
SUBACK Visibility OnSubscribeAck exposes broker-granted QoS per filter
Anonymous + Method Pointer Handlers Every event accepts both reference to procedure and procedure of object
Thread-safe Background receiver + pinger tasks, locked state machine
Cross-platform Windows, Linux, macOS (Indy provides the transport)
Zero deps Only Indy (bundled with Delphi); LoggerPro adapter is opt-in

Why Delphi MQTT?

  • Native — no DLL wrappers, no COM, no FFI. Pure Object Pascal on top of Indy.
  • Production-ready — used in IoT gateways, telemetry pipelines, and desktop control panels. 61-test DUnitX suite plus cross-protocol round-trip tests against localhost, test.mosquitto.org, broker.hivemq.com and broker.emqx.io.
  • Open standards — speaks the broker, no proprietary extensions. Interoperates over WebSocket with browser pages running MQTT.js (see sample 12_WebChat).
  • Honest defaultsNullLogger by default (zero overhead), opt-in to logging via LoggerPro or any backend you wrap in an adapter.

Table of Contents

  1. Installation
  2. Quick Start
  3. Connection Options
  4. SSL / TLS Connections
  5. Quality of Service (QoS)
  6. Subscribe Modes
  7. Topic Wildcards
  8. Automatic Reconnection
  9. Last Will and Testament
  10. Logging
  11. Packet Monitoring
  12. Subscription Acknowledgment
  13. Anonymous Methods vs Method Pointers
  14. Threading Model
  15. API Reference
  16. Samples
  17. Building with msbuild
  18. Testing
  19. Bridging to Web Pages (MQTT over WebSocket)
  20. Troubleshooting
  21. Links

Installation

Clone the repository and add src\ to your project’s unit search path:

git clone https://github.com/danieleteti/delphi-mqtt.git

The four core units are:

src\MQTT.Types.pas              :: type definitions, exceptions, enums
src\MQTT.Protocol.pas           :: MQTT protocol encoding/decoding
src\MQTT.Logger.pas             :: IMQTTLogger + Null/Proc impls (zero deps)
src\MQTT.Client.pas             :: high-level client implementation

And one optional unit:

src\MQTT.Logger.LoggerPro.pas   :: adapter for LoggerPro (only if you use it)

Requirements:

  • Delphi 10.3 Rio or newer (RAD Studio 12 Athens recommended)
  • Indy TCP components (standard in Delphi)
  • An MQTT broker (e.g. Mosquitto) for runtime testing
  • OpenSSL DLLs (libeay32.dll, ssleay32.dll) only if you use TLS

Quick Start

uses MQTT.Client, MQTT.Types;

var
  Client: IMQTTClient;
begin
  Client := CreateMQTTClient;
  Client.Connect('localhost', 1883);

  Client.Subscribe('sensors/+/temperature',
    procedure(const Topic: string; const Payload: TBytes)
    begin
      Writeln(Topic, ' = ', TEncoding.UTF8.GetString(Payload));
    end,
    atLeastOnce);

  Client.Publish('sensors/kitchen/temperature', '23.5', atLeastOnce);

  Client.Disconnect;
end;

CreateMQTTClient returns an IMQTTClient interface so you do not have to free the instance manually - the standard Delphi reference counting takes care of it.


Connection Options

var
  Client: IMQTTClient;
  Options: TMQTTConnectOptions;
begin
  Client := CreateMQTTClient;

  Options.SetDefaults;
  Options.ClientID := 'MyDelphiApp';
  Options.KeepAliveSec := 60;
  Options.CleanStart := True;
  Options.Version := MQTT5;          // or MQTT311
  Options.Username := 'user';
  Options.Password := 'pass';

  Client.AutoReconnect := True;

  Client.SetOnConnect(procedure(ReasonCode: TMQTTReasonCode)
  begin
    Writeln('Connected!');
  end);

  Client.SetOnDisconnect(procedure(ReasonCode: TMQTTReasonCode; const Reason: string)
  begin
    Writeln('Disconnected: ', Reason);
  end);

  Client.SetOnError(procedure(const ErrorMsg: string)
  begin
    Writeln('Error: ', ErrorMsg);
  end);

  Client.Connect('localhost', 1883, Options);
end;

TMQTTConnectOptions fields

Field Type Meaning
ClientID string Client identifier
KeepAliveSec Word Keep-alive interval in seconds (default 60)
CleanStart Boolean Clean session flag (default True)
Version TMQTTVersion MQTT311 or MQTT5 (default MQTT5)
Username / Password string Authentication credentials
Will TMQTTWillOptions Last Will and Testament configuration
SessionExpiryInterval Cardinal MQTT 5 session expiry
ReceiveMaximum Word MQTT 5 receive maximum
MaxPacketSize Cardinal MQTT 5 max packet size
TopicAliasMaximum Word MQTT 5 topic alias maximum

SSL / TLS Connections

Basic SSL

Client.ConnectSSL('test.mosquitto.org', 8883);

Or fully configured:

var
  SSLOptions: TMQTTSSLOptions;
begin
  SSLOptions.SetDefaults;
  SSLOptions.Enabled := True;
  SSLOptions.Method := sslTLS1_2;
  SSLOptions.VerifyMode := sslVerifyNone;   // sslVerifyPeer in production

  Client.Connect('test.mosquitto.org', 8883, Options, SSLOptions);
end;

Mutual TLS (AWS IoT / Azure IoT Hub)

SSLOptions.SetDefaults;
SSLOptions.Enabled := True;
SSLOptions.Method := sslTLS1_2;
SSLOptions.VerifyMode := sslVerifyPeer;
SSLOptions.CertFile := 'certs/client.crt';
SSLOptions.KeyFile := 'certs/client.key';
SSLOptions.RootCertFile := 'certs/AmazonRootCA1.pem';
SSLOptions.KeyPassword := '';   // if the key is encrypted

Client.Connect('your-endpoint.iot.region.amazonaws.com', 8883, Options, SSLOptions);

OpenSSL DLLs

The Indy SSL layer requires the legacy OpenSSL 1.0.2 DLLs: libeay32.dll and ssleay32.dll. The OpenSSL 1.1.x / 3.x split DLLs (libssl-1_1-x64.dll, libcrypto-1_1-x64.dll) are NOT compatible with Indy’s default SSL implementation.

Download from https://slproweb.com/products/Win32OpenSSL.html and drop both DLLs next to your .exe.

Public SSL brokers tested

Broker Host Port Status
Eclipse Mosquitto test.mosquitto.org 8883 ✅ Working
HiveMQ Public broker.hivemq.com 8883 ✅ Working
EMQX Public broker.emqx.io 8883 ⚠️ Sometimes flaky
Eclipse IoT mqtt.eclipseprojects.io 8883 ⚠️ Sometimes flaky

The repository’s samples/10_SSL/TestAllBrokers.dpr re-runs this matrix on demand from your network.


Quality of Service (QoS)

Level Name Description
0 At Most Once Fire and forget, no acknowledgment
1 At Least Once Guaranteed delivery via PUBACK (possibly duplicated)
2 Exactly Once Guaranteed single delivery via 4-way handshake
Client.Publish('topic', 'data',      atMostOnce);     // QoS 0
Client.Publish('topic', 'important', atLeastOnce);    // QoS 1
Client.Publish('topic', 'critical',  exactlyOnce);    // QoS 2

if Client.PublishSync('topic', Payload, atLeastOnce, False, 5000) then
  Writeln('Message acknowledged!')
else
  Writeln('Timeout / no ACK.');

Dup flag and redelivery

QoS 1 and 2 messages may be redelivered if an acknowledgment is lost. The Dup flag exposed by SubscribeEx lets your handler detect this:

Client.SubscribeEx('orders/#',
  procedure(const Topic: string; const Payload: TBytes; Dup: Boolean; QoS: TMQTTQoS)
  var OrderID: string;
  begin
    OrderID := ExtractOrderID(Payload);
    if Dup and OrderAlreadyProcessed(OrderID) then
      Exit;                              // skip duplicate processing
    ProcessOrder(Payload);
    MarkOrderAsProcessed(OrderID);
  end,
  atLeastOnce);

Subscribe Modes

The library offers three flavours, each available with both anonymous methods and method pointers (of object).

Standard Subscribe (auto-ACK)

Client.Subscribe('topic/#',
  procedure(const Topic: string; const Payload: TBytes)
  begin
    Writeln('Received: ', TEncoding.UTF8.GetString(Payload));
  end,
  atLeastOnce);

The library dispatches your handler FIRST, then sends the PUBACK to the broker. This preserves the at-least-once to the application guarantee: if the process dies between receive and ACK, the broker redelivers.

SubscribeEx (extended)

Receives the Dup flag and the actual QoS in addition to topic + payload:

Client.SubscribeEx('topic/#',
  procedure(const Topic: string; const Payload: TBytes; Dup: Boolean; QoS: TMQTTQoS)
  begin
    if Dup then Writeln('WARN: redelivery');
  end,
  atLeastOnce);

SubscribeManualAck

Fine-grained control over acknowledgment. Set Ack := True to PUBACK, leave it False to let the broker redeliver:

Client.SubscribeManualAck('critical/events',
  procedure(const Topic: string; const Payload: TBytes; Dup: Boolean; var Ack: Boolean)
  begin
    try
      ProcessMessage(Payload);
      Ack := True;
    except
      Ack := False;     // broker redelivers
    end;
  end,
  atLeastOnce);

Topic Wildcards

Client.Subscribe('home/+/temperature', Handler);
// + matches exactly one level:
//   home/kitchen/temperature   - YES
//   home/temperature           - NO
//   home/floor1/room2/temperature - NO

Client.Subscribe('sensors/#', Handler);
// # matches any number of levels (including zero):
//   sensors                    - YES
//   sensors/temp               - YES
//   sensors/temp/indoor        - YES

Client.Subscribe('+/status/#', Handler);
// combined: device1/status, device1/status/cpu, pump/status/flow/rate

Automatic Reconnection

Client.AutoReconnect := True;

The reconnect loop uses exponential backoff (1s → 2s → 4s → 8s → 16s → capped at 30s). After a successful reconnect the client:

  1. restarts the keep-alive pinger,
  2. re-sends SUBSCRIBE for every subscription stored locally (because with CleanStart = True the broker forgot them),
  3. retries pending QoS 1 / 2 publishes,
  4. fires OnConnect again.

If you also need to do additional work on reconnect (re-publish a “hello” message, refresh local state, etc.), put it in the OnConnect handler — it fires on both initial connect and every reconnect.


Last Will and Testament

Options.Will.Enabled := True;
Options.Will.Topic := 'clients/MyDelphiApp/status';
Options.Will.Payload := TEncoding.UTF8.GetBytes('OFFLINE');
Options.Will.QoS := atLeastOnce;
Options.Will.Retain := True;
Options.Will.DelayInterval := 30;   // MQTT 5 only

If the client disconnects unexpectedly (crash, network drop, machine power-off), the broker publishes the will message on the will topic so other subscribers know you’re gone.


Logging

The client exposes the IMQTTLogger interface so the core library stays free of any external logging dependency. Two trivial implementations ship in MQTT.Logger:

  • TMQTTNullLogger — the default, no-op, zero overhead.
  • TMQTTProcLogger — forwards each call to a reference to procedure. Useful from samples and quick scripts.

For production, plug your existing logging framework via an adapter. The repository ships a ready adapter for LoggerPro in src/MQTT.Logger.LoggerPro.pas.

Log levels: llDebug, llInfo, llWarning, llError, llNone.

Inline procedure logger

Client.Logger := CreateProcLogger(
  procedure(Level: TMQTTLogLevel; const Msg: string)
  begin
    Writeln(Format('[%-5s] %s', [LogLevelName(Level), Msg]));
  end,
  llDebug);

LoggerPro adapter (async)

uses
  LoggerPro, LoggerPro.ConsoleAppender, LoggerPro.FileAppender,
  MQTT.Logger.LoggerPro;

var
  Log: ILogWriter;
begin
  Log := BuildLogWriter([
    TLoggerProConsoleAppender.Create,
    TLoggerProFileAppender.Create(5, 1000, 'logs')
  ]);

  Client.Logger := WrapLoggerPro(Log, 'MQTT', llDebug);
end;

The adapter tags every entry (default 'MQTT') so LoggerPro formatters can split MQTT traffic from the rest of your app. See samples/13_LoggerPro/ for a runnable example.

Sample log output:

2026-05-16 18:31:10:473 [TID  3152][DEBUG] TX CONNECT (27 bytes)    [MQTT]
2026-05-16 18:31:10:473 [TID  3152][DEBUG] RX CONNACK (4 bytes)     [MQTT]
2026-05-16 18:31:10:473 [TID  3152][INFO ] Connected to localhost...[MQTT]
2026-05-16 18:31:10:473 [TID  3152][INFO ] SUBSCRIBE PacketID=1 ... [MQTT]
2026-05-16 18:31:10:473 [TID 28720][DEBUG] RX SUBACK (5 bytes)      [MQTT]
2026-05-16 18:31:10:473 [TID 28720][INFO ] SUBACK PacketID=1 GrantedQoS=[1] [MQTT]

Packet Monitoring

Hook every inbound and outbound packet for debugging, metrics, or audit logs. Both events fire after the I/O has happened, so the cost is limited to your callback.

Client.SetOnPacketSent(
  procedure(PT: TMQTTPacketType; const Raw: TBytes)
  begin
    Writeln('TX ', GetEnumName(TypeInfo(TMQTTPacketType), Ord(PT)),
            ' (', Length(Raw), ' bytes)');
  end);

Client.SetOnPacketReceived(
  procedure(PT: TMQTTPacketType; const Raw: TBytes)
  begin
    // count / dump / store / forward / ...
  end);

Subscription Acknowledgment

SUBSCRIBE returns the QoS the broker actually granted - which may be lower than what you asked for, or be a rejection code (>= 0x80).

Client.SetOnSubscribeAck(
  procedure(PacketID: Word; const GrantedQoS: TArray<Byte>)
  var Code: Byte;
  begin
    for Code in GrantedQoS do
      if Code >= $80 then
        Writeln('Subscription rejected: code=', Code)
      else
        Writeln('Granted QoS=', Code);
  end);

Anonymous Methods vs Method Pointers

Every event and every subscribe method accepts both reference to procedure (anonymous) and procedure of object (method pointer) handlers. Pick the one that fits your codebase.

type
  TMyForm = class(TForm)
  private
    FClient: IMQTTClient;
    procedure HandleMessage(const Topic: string; const Payload: TBytes);
    procedure HandleDisconnect(ReasonCode: TMQTTReasonCode; const ReasonString: string);
  end;

procedure TMyForm.FormCreate(Sender: TObject);
begin
  FClient := CreateMQTTClient;
  FClient.Subscribe('my/topic', HandleMessage, atLeastOnce);
  FClient.SetOnDisconnect(HandleDisconnect);
end;

procedure TMyForm.HandleMessage(const Topic: string; const Payload: TBytes);
begin
  Memo1.Lines.Add(TEncoding.UTF8.GetString(Payload));
end;

Threading Model

All message handlers and event callbacks run on a background thread (the internal receiver task). For UI updates use TThread.Queue or TThread.Synchronize:

Client.Subscribe('topic',
  procedure(const Topic: string; const Payload: TBytes)
  begin
    // background thread
    TThread.Queue(nil, procedure
    begin
      // main UI thread
      Memo1.Lines.Add(TEncoding.UTF8.GetString(Payload));
    end);
  end);

The keep-alive pinger runs on its own task and is restarted automatically after every reconnection.


API Reference

Factory function

function CreateMQTTClient: IMQTTClient;

IMQTTClient interface (excerpt)

// Connection
procedure Connect(const Host: string; Port: Word = 1883);
procedure Connect(const Host: string; Port: Word; const Options: TMQTTConnectOptions);
procedure Connect(const Host: string; Port: Word; const Options: TMQTTConnectOptions;
                  const SSLOptions: TMQTTSSLOptions);
procedure ConnectSSL(const Host: string; Port: Word = 8883);
procedure Disconnect;
function IsConnected: Boolean;
function GetState: TMQTTConnectionState;

// Publishing
procedure Publish(const Topic, Payload: string; QoS: TMQTTQoS = atMostOnce;
                  Retain: Boolean = False);
procedure Publish(const Topic: string; const Payload: TBytes; QoS: TMQTTQoS = atMostOnce;
                  Retain: Boolean = False);
function  PublishSync(const Topic: string; const Payload: TBytes; QoS: TMQTTQoS;
                      Retain: Boolean; TimeoutMs: Cardinal = 5000): Boolean;

// Subscribing - three modes, each with anonymous + method-pointer overload
procedure Subscribe          (const Topic: string; Handler; QoS: TMQTTQoS = atMostOnce);
procedure SubscribeEx        (const Topic: string; Handler; QoS: TMQTTQoS = atMostOnce);
procedure SubscribeManualAck (const Topic: string; Handler; QoS: TMQTTQoS = atLeastOnce);
procedure Unsubscribe        (const Topic: string);

// Event hooks
procedure SetOnConnect       (Handler);
procedure SetOnDisconnect    (Handler);
procedure SetOnError         (Handler);
procedure SetOnPacketSent    (Handler);
procedure SetOnPacketReceived(Handler);
procedure SetOnSubscribeAck  (Handler);

// Configuration
property AutoReconnect: Boolean;
property Logger: IMQTTLogger;
property Connected: Boolean read IsConnected;
property State: TMQTTConnectionState read GetState;

Handler types (MQTT.Types.pas)

// Anonymous methods
TMQTTHandler          = reference to procedure(const Topic: string; const Payload: TBytes);
TMQTTExtendedHandler  = reference to procedure(const Topic: string; const Payload: TBytes;
                                               Dup: Boolean; QoS: TMQTTQoS);
TMQTTManualAckHandler = reference to procedure(const Topic: string; const Payload: TBytes;
                                               Dup: Boolean; var Ack: Boolean);
TMQTTPacketHandler    = reference to procedure(PacketType: TMQTTPacketType;
                                               const RawPacket: TBytes);
TMQTTSubscribeAckHandler = reference to procedure(PacketID: Word;
                                                  const GrantedQoS: TArray<Byte>);

// Method pointers (each handler above also has a "of object" twin)
TMQTTMessageEvent     = procedure(const Topic: string; const Payload: TBytes) of object;
TMQTTExtendedEvent    = procedure(const Topic: string; const Payload: TBytes;
                                  Dup: Boolean; QoS: TMQTTQoS) of object;
// ...

TMQTTSSLOptions

TMQTTSSLOptions = record
  Enabled: Boolean;
  CertFile: string;             // client certificate (PEM)
  KeyFile: string;              // private key (PEM)
  RootCertFile: string;         // CA root certificate (PEM)
  KeyPassword: string;          // for encrypted private key
  Method: TMQTTSSLMethod;       // sslAuto, sslTLS1, sslTLS1_1, sslTLS1_2, sslTLS1_3
  VerifyMode: TMQTTSSLVerifyMode;  // sslVerifyNone, sslVerifyPeer
  VerifyDepth: Integer;
  procedure SetDefaults;
end;

Samples

The samples/ folder ships 13 runnable projects:

# Folder What it shows
01 01_Connect Basic connect/disconnect lifecycle
02 02_PublishSubscribe Publish/subscribe round-trip
03 03_Performance Throughput benchmark with TStopwatch
04 04_QoS All three QoS levels side by side
05 05_Wildcards + and # filter patterns
06 06_Reconnection AutoReconnect + re-subscribe behavior
07 07_WillMessage Last Will & Testament
08 08_MethodPointers procedure of object handlers
09 09_VCL_Chat Multi-instance VCL chat application
10 10_SSL TLS + mutual TLS + public broker matrix
11 11_Logging IMQTTLogger interface + packet events + SUBACK
12 12_WebChat Delphi (TCP) + 2 browser pages (WebSocket) chat
13 13_LoggerPro Production logging via LoggerPro adapter

Each sample compiles to a standalone .exe with msbuild.


Building with msbuild

All projects in the repository are real Delphi .dproj files (you can open them in the IDE) but the canonical build path is msbuild. The repository ships a Python orchestrator that handles the RAD Studio environment setup:

python scripts\build-all.py                     :: Debug / Win64, every .dproj
python scripts\build-all.py --config Release
python scripts\build-all.py --platform Win32
python scripts\build-all.py --only tests        :: filter by path substring

Helper scripts keep the .dproj files in sync with the source unit list:

  • scripts/patch_dprojs.py — idempotent patcher (adds the MQTT.Logger.pas reference, fixes unit search paths, enables Win64 platform groups).
  • scripts/regen_dprojs.py + scripts/dproj_template.py — generate fresh .dproj files for new samples / tests.

Testing

The library ships a DUnitX test suite in tests/. Four fixtures, 61 tests total:

Suite Coverage Broker
MQTTProtocolTests Variable-length encoding, UTF-8 strings, wildcard matching, packet build/parse round-trips No
MQTTLoggerTests TMQTTNullLogger, TMQTTProcLogger, level filtering, exception swallowing No
MQTTClientTests Connect lifecycle, QoS 0/1/2 round-trip, wildcard subscribe, packet events, SUBACK event, retain flag, logger integration localhost:1883
MQTTPublicBrokerTests Plain + SSL round-trip against test.mosquitto.org, broker.hivemq.com, broker.emqx.io Internet, OpenSSL DLLs for SSL

Public broker tests distinguish network-class failures from protocol-class failures: if a host is unreachable the test soft-skips, but if a broker is reachable yet the round-trip protocol fails, the test reports a real failure (no silent passes hiding bugs).

python scripts\build-all.py --only tests
tests\MqttTests.exe --exitbehavior:Continue

For SSL public-broker tests, copy libeay32.dll and ssleay32.dll next to MqttTests.exe.

Cross-protocol tests (TCP ↔ WebSocket)

scripts/cross-protocol-tests/run_cross_tests.py orchestrates three round-trip scenarios between the Delphi MQTT client and a Python paho-mqtt WebSocket client through the same Mosquitto broker:

delphi -> delphi      (TCP -> TCP)
delphi -> websocket   (TCP pub -> WS sub)
websocket -> delphi   (WS pub -> TCP sub)

Run it after enabling the Mosquitto WebSocket listener:

python scripts\mosquitto\apply-websockets.py        :: requires admin
python scripts\build-all.py --only cross-protocol-tests
python scripts\cross-protocol-tests\run_cross_tests.py

Bridging to Web Pages (MQTT over WebSocket)

Delphi MQTT speaks plain TCP MQTT. Browsers cannot open raw TCP sockets, so they need MQTT over WebSocket, which most modern brokers support natively. The pattern: configure your broker with both a plain TCP listener (port 1883) and a WebSocket listener (e.g. port 9001), let your Delphi client connect over TCP and your browser pages over WebSocket — they all see each other on the same topics.

Sample 12_WebChat demonstrates a full three-way chat where a Delphi console client and two HTML pages (using MQTT.js) exchange JSON messages on a shared topic, with retained presence and Last Will. The browser pages connect to ws://localhost:9001/mqtt; the Delphi client connects to tcp://localhost:1883. Mosquitto bridges them.

The repository includes a helper (scripts/mosquitto/apply-websockets.py) that appends the WebSocket listener to your local Mosquitto config and restarts the service.


Troubleshooting

F2613: Unit 'MQTT.Logger' not found — your .dproj is missing MQTT.Logger.pas from its DCCReference list, or ..\src is not on its DCC_UnitSearchPath. Run python scripts/patch_dprojs.py from the repo root to fix it idempotently.

No CONNACK from broker — broker reachable but doesn’t speak the protocol version you asked for. Try Options.Version := MQTT311.

SSL handshake failed on Mosquitto / HiveMQ — wrong OpenSSL DLLs. Indy needs the legacy 1.0.2 build (libeay32.dll, ssleay32.dll), not the 1.1.x / 3.x split (libssl-1_1-x64.dll).

Messages stop arriving after a network drop — check that AutoReconnect := True is set; the client re-subscribes automatically on reconnect.

Public broker tests skip instead of running — check that the host is reachable from your network (Test-NetConnection broker.hivemq.com -Port 1883). Public ports are sometimes filtered by corporate firewalls.



Delphi MQTT is open source under the Apache 2.0 license. Maintained by Daniele Teti. Bug reports and pull requests welcome on GitHub.

Comments

comments powered by Disqus