QtKnx: tutorial and updates

In this tutorial we will demonstrate how to write a KNX client requesting a local device management procedure to a KNX server. We will be using the QtKnx module that comes with the Automation package.

Sequence diagram

The following diagram presents the sequence of KNX messages exchanged between the client and the server.

no-title-2

The third message in the sequence contains the actual management procedure request. In this tutorial the client is initiating a property read request. It is not until the fifth message, that the client receives the property read confirmation encapsulated in a DEVICE_CONFIGURATION_REQUEST.

Server implementation

First, the server needs a QUdpSocket to listen for incoming connections from the KNX client.  We need to call the QUDPsocket::bind() operation to achieve the expected behavior. The server will listen on port 3671 and the client on 3672. Here is the server code for this part:

serverSocket = new QUdpSocket();
serverSocket->bind(QHostAddress("0.0.0.0"), 3671);

The server is now able to listen for incoming connections but the logic for processing incoming management messages is missing. Therefore, we need to connect to the QUdpSocket::readyRead signal that will be triggered every time a new datagram arrives on the bounded network interface. Translated into code this shall look something like this:

QObject::connect(serverSocket, &QUdpSocket::readyRead,
    [&]() {
    while (serverSocket
            && serverSocket->hasPendingDatagrams()) {
        auto datagram = serverSocket->receiveDatagram();
        auto data = QKnxByteArray::fromByteArray(datagram.data());
        auto knxNetIpframe = QKnxNetIpFrame::fromBytes(data);

handleFrame(knxNetIpframe); } });

The function handleFrame used above contains the logic for managing KNX messages received. Simplifying the implementation it could look like this:

void handleFrame(QKnxNetIpFrame &frame) {
    switch (frame.serviceType()) {
        case QKnxNetIp::ServiceType::DisconnectResponse: {
            // ...
            break;
        }
        case QKnxNetIp::ServiceType::ConnectRequest: {
            // ...
            break;
        }
        case QKnxNetIp::ServiceType::DisconnectRequest: {
            // ...
            break;
        }
        case QKnxNetIp::ServiceType::DeviceConfigurationRequest: {
            // ...
            break;
        }
        case QKnxNetIp::ServiceType::DeviceConfigurationAcknowledge: {
            // ...
            break;
        }
        default:
        break;
    }
}

For example, when a disconnect request is received the server answers back with a disconnect response, at the same time the server terminates.

auto frame = QKnxNetIpDisconnectResponseProxy::builder()
            .setChannelId(200)
            .setStatus(QKnxNetIp::Error::None)
            .create();

serverSocket->writeDatagram(frame.bytes().toByteArray(), QHostAddress(ipClient), 3672);

QCoreApplication::quit();

If a Connect Request arrives the server will respond with a Connect Response:

auto frame = QKnxNetIpConnectResponseProxy::builder()
            .setChannelId(200)
            .setStatus(QKnxNetIp::Error::None)
            .setDataEndpoint(
                QKnxNetIpHpaiProxy::builder()
                .setHostAddress(ipClient)
                .setPort(3672)
                .setHostProtocol(QKnxNetIp::HostProtocol::UDP_IPv4)
                .create())
            .setResponseData(
                QKnxNetIpCrdProxy::builder()
                .setConnectionType(QKnxNetIp::ConnectionType::DeviceManagement)
                .create())
            .create();

The property read is received within a device configuration request message. After acknowledging the property read the server can answer with a property read confirmation. This message is built like this :

auto responseFrame = QKnxDeviceManagementFrame::propertyReadBuilder()
                     .setObjectType(QKnxInterfaceObjectType::System::Device)
                     .setObjectInstance(1)
                     .setProperty(QKnxInterfaceObjectProperty::General::SerialNumber)
                     .setNumberOfElements(1)
                     .setStartIndex(1)
                     .createConfirmation(QKnxByteArray::fromHex("00027c04b550"));

The createConfirmation() method above, receives as parameter the serial number of the KNX server ("0x00027c04b550").

Client implementation

Initially, the client needs an instance of QKnxNetIpDeviceManagement. This class abstracts us from having to create and handle the local device management connection.

QKnxNetIpDeviceManagement devManager;

When the devManager instance emits the connected signal the client will send the KNX property read frame. This is done here:

QObject::connect(&devManager, &QKnxNetIpDeviceManagement::connected,
    [&] () {
    auto frame = QKnxDeviceManagementFrame::propertyReadBuilder()
                .setObjectType(QKnxInterfaceObjectType::System::Device)
                .setObjectInstance(1)
                .setProperty(QKnxInterfaceObjectProperty::General::SerialNumber)
                .setNumberOfElements(1)
                .setStartIndex(1)
                .createRequest();
                devManager.sendFrame(frame);
});

The client establishes the connection to the KNX server by calling connectToHost() on the device management object instance. This call requires a QKnxNetIpHpai parameter that contains the ip and port of the server, as seen in this code:

QLatin1String ipLocal (ipClient);

devManager.setLocalAddress(QHostAddress(ipLocal)); devManager.setLocalPort(3671);

QLatin1String ipServer ("127.0.0.1");

devManager.connectToHost(QKnxNetIpHpaiProxy::builder() .setHostAddress(QHostAddress(ipServer)) .setPort(3671) .create());

 

QtKnx updates in 5.12

Stay tuned for the coming 5.12 release. This time it will come with a lot of
new changes:

  • Added support for KNXnet/IP Core v2: extending discovery functionality and support for secure communications using TCP.
  • Included support for KNXnet/IP Tunneling v2: added new structures, properties and messages.
  • Introduced a KNXnet/IP router class for supporting KNX routing. We also included a new example for it.
  • Enabled security configuration in IP sub networks over system broadcast communication mode.
  • Started enabling support for KNXnet/IP Security: encryption of KNXnet/IP traffic.
  • And lastly, we spend a great effort expanding the documentation with all the new features and examples.

Please give us your feedback!


Blog Topics:

Comments