Quick CoAP Multicast Discovery Example▲
The Quick CoAP Multicast Discovery Example demonstrates how to register QCoapClient as a QML type and use it in a Qt Quick application for CoAP multicast resource discovery.
Qt CoAP does not provide a QML API in its current version. However, you can make the C++ classes of the module available to QML as shown in this example.
Running the Example▲
To run the example application, you first need to set up and start at least one CoAP server supporting multicast resource discovery. You have the following options:
-
Manually build and run CoAP servers using libcoap, Californium, or any other CoAP server implementation, which supports multicast and resource discovery features.
-
Use the ready Docker image available at Docker Hub, which builds and starts CoAP server based on Californium's multicast server example.
Using the Docker-based Test Server▲
The following command pulls the docker container for the CoAP server from the Docker Hub and starts it:
docker run --name coap-multicast-server -d --rm --net=host sokurazy/coap-multicast-test-server:californium.2.0.xYou can run more than one multicast CoAP servers (on the same host or other hosts in the network) by passing a different --name to the command above.
Creating a Client and Using It with QML▲
We create the QmlCoapMulticastClient class with the QCoapClient class as a base class:
class QmlCoapMulticastClient : public QCoapClient
{
Q_OBJECT
public:
QmlCoapMulticastClient(QObject *parent = nullptr);
Q_INVOKABLE void discover(const QString &host, int port, const QString &discoveryPath);
Q_INVOKABLE void discover(QtCoap::MulticastGroup group, int port, const QString &discoveryPath);
Q_SIGNALS:
void discovered(const QmlCoapResource &resource);
void finished(int error);
public slots:
void onDiscovered(QCoapResourceDiscoveryReply *reply, const QList<QCoapResource> &resources);
};In the main.cpp file, we register the QmlCoapMulticastClient class as a QML type:
qmlRegisterType<QmlCoapMulticastClient>("CoapMulticastClient", 1, 0, "CoapMulticastClient");We also register the QtCoap namespace, to be able to use it in QML code:
qmlRegisterUncreatableMetaObject(QtCoap::staticMetaObject, "qtcoap.example.namespace", 1, 0,
"QtCoap", "Access to enums is read-only");Now in the QML code, we can import and use these types:
...
import CoapMulticastClient
import qtcoap.example.namespace
...
CoapMulticastClient {
id: client
onDiscovered: addResource(resource)
onFinished: {
statusLabel.text = (error === QtCoap.Error.Ok)
? qsTr("Finished resource discovery.")
: qsTr("Resource discovery failed with error code: %1").arg(error)
}
onError:
statusLabel.text = qsTr("Resource discovery failed with error code: %1").arg(error)
}
...The QCoapClient::error() signal triggers the onError signal handler of CoapMulticastClient, and the QmlCoapMulticastClient::finished() signal triggers the onFinished signal handler, to show the request's status in the UI. Note that we are not using the QCoapClient::finished() signal directly, because it takes a QCoapReply as a parameter (which is not a QML type), and we are interested only in the error code.
In the QmlCoapMulticastClient's constructor, we arrange for the QCoapClient::finished() signal to be forwarded to the QmlCoapMulticastClient::finished() signal:
QmlCoapMulticastClient::QmlCoapMulticastClient(QObject *parent)
: QCoapClient(QtCoap::SecurityMode::NoSecurity, parent)
{
connect(this, &QCoapClient::finished, this,
[this](QCoapReply *reply) {
if (reply)
emit finished(static_cast<int>(reply->errorReceived()));
else
qCWarning(lcCoapClient, "Something went wrong, received a null reply");
});
}When the Discover button is pressed, we invoke one of the overloaded discover() methods, based on the selected multicast group:
...
Button {
id: discoverButton
text: qsTr("Discover")
Layout.columnSpan: 2
onClicked: {
var currentGroup = groupComboBox.model.get(groupComboBox.currentIndex).value;
var path = "";
if (currentGroup !== - 1) {
client.discover(currentGroup, parseInt(portField.text),
discoveryPathField.text);
path = groupComboBox.currentText;
} else {
client.discover(customGroupField.text, parseInt(portField.text),
discoveryPathField.text);
path = customGroupField.text + discoveryPathField.text;
}
statusLabel.text = qsTr("Discovering resources at %1...").arg(path);
}
}
...This overload is called when a custom multicast group or a host address is selected:
void QmlCoapMulticastClient::discover(const QString &host, int port, const QString &discoveryPath)
{
QUrl url;
url.setHost(host);
url.setPort(port);
QCoapResourceDiscoveryReply *discoverReply = QCoapClient::discover(url, discoveryPath);
if (discoverReply) {
connect(discoverReply, &QCoapResourceDiscoveryReply::discovered,
this, &QmlCoapMulticastClient::onDiscovered);
} else {
qCWarning(lcCoapClient, "Discovery request failed.");
}
}And this overload is called when one of the suggested multicast groups is selected in the UI:



