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
CreateMQTTClientto get anIMQTTClient, callConnect(host, port[, options[, ssl]]), thenSubscribe(topic, handler, qos)andPublish(topic, payload, qos[, retain]). Plain TCP (Connect, port 1883) and TLS (ConnectSSL, port 8883) are first-class, mutual TLS viaTMQTTSSLOptions.CertFile/KeyFile/RootCertFile. QoS 0/1/2 with full acknowledgment flows.IMQTTLoggerinterface decouples logging; ship the separateMQTT.Logger.LoggerProadapter 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.comandbroker.emqx.io. - Open standards — speaks the broker, no proprietary extensions.
Interoperates over WebSocket with browser pages running MQTT.js (see
sample
12_WebChat). - Honest defaults —
NullLoggerby default (zero overhead), opt-in to logging via LoggerPro or any backend you wrap in an adapter.
Table of Contents
- Installation
- Quick Start
- Connection Options
- SSL / TLS Connections
- Quality of Service (QoS)
- Subscribe Modes
- Topic Wildcards
- Automatic Reconnection
- Last Will and Testament
- Logging
- Packet Monitoring
- Subscription Acknowledgment
- Anonymous Methods vs Method Pointers
- Threading Model
- API Reference
- Samples
- Building with msbuild
- Testing
- Bridging to Web Pages (MQTT over WebSocket)
- Troubleshooting
- 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:
- restarts the keep-alive pinger,
- re-sends
SUBSCRIBEfor every subscription stored locally (because withCleanStart = Truethe broker forgot them), - retries pending QoS 1 / 2 publishes,
- fires
OnConnectagain.
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 areference 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 theMQTT.Logger.pasreference, fixes unit search paths, enables Win64 platform groups).scripts/regen_dprojs.py+scripts/dproj_template.py— generate fresh.dprojfiles 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.
Links
- GitHub repository: https://github.com/danieleteti/delphi-mqtt
- License: Apache 2.0
- LoggerPro adapter: https://www.danieleteti.it/loggerpro/
- MQTT 5.0 specification: https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html
- MQTT 3.1.1 specification: https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html
- MQTT.js (browser): https://github.com/mqttjs/MQTT.js
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