Marc Silanus

MQTT avec Qt

Posted on 22 juillet 2018

MQTT est un protocole de connectivité Machine-to-Machine (M2M) de l'Internet des Objets. Il a été conçu pour le transport de messages par publication/souscription et est extrêmement léger.

The Things Network utilise MQTT pour publier les activations et les messages des nœuds LoRa qu'il référence. Nous avons vu dans l'article précédant comment accéder à ses messages via le nœud MQTT de Node Red.

Je vous propose ici de réaliser une application client avec Qt qui accèdera directement aux messages via MQTT au travers de l'utilisation du module Qt MQTT.

Installation

Le module Qt MQTT n'est installé par défaut que dans la version commerciale de Qt spécialisée Qt for Automation (Transform your automation strategy). Il est en revanche possible de l'installer manuellement à partir de ses sources :

git://code.qt.io/qt/qtmqtt.git

Téléchargez les sources :

git clone  git://code.qt.io/qt/qtmqtt.git
cd qtmqtt

Double-cliquez sur le qtmqtt.pro pour ouvrir le projet dans Qt Creator.

Cliquez sur Projets puis ajouter une étape Make aux étapes de compilation avec comme argument : install

Exemple simple client MQTT

Le module dispose de programme d'exemple. Dans le dossier qtmqtt/exemples/mqtt/simpleclient, double cliquez sur simpleclient.pro pour ouvrir le projet dans Qt. Remarque, veillez à fermer le projet en cours (clique droit à la racine du projet et Fermer le projet).

Compilez et exécutez le programme :

Super, ça marche ! Mais on écrit quoi dans le formulaire ? Voyons un peu le code :

Ouvrez le fichier mainwindow.cpp et observez le constructeur :

On y trouve un objet m_client de la classe QMqttClient dont la définition de l'hôte à connecter se fait par l'appel à la méthode setHostName() et la définition du port par l'appel à la méthode setPort().

Documentation de la classe QMqttClient.

Dans l'article précédant, nous avons utilisé le broker MQTT de The Things Network dont les paramètres sont les suivants :

  • Host: <Region>.thethings.network, where <Region> is last part of the handler you registered your application to, e.g. eu.
  • Port: 1883
  • Username: Application ID
  • Password: Application Access Key
  • Topic: <AppID>/devices/<DevID>/up

Dans le code de l'exemple, il n'est pas prévu d'utiliser une connexion sécurisée. Il nous faut donc ajouter le code nécessaire pour indiquer le nom d'utilisateur et le mot de passe.

m_client->setUsername(QString);
m_client->setPassword(QString);
Méthodes pour une connexion authentifiée

Ajustons donc le code relativement aux indications de la documentation de The Things Network et à la configuration de l'application et du devices déclaré dans le console TTN vu dans l'article précédant.

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    m_client = new QMqttClient(this);
    ui->lineEditHost->setText("eu.thethings.network");
    m_client->setHostname(ui->lineEditHost->text());
    m_client->setPort(ui->spinBoxPort->value());

    m_client->setUsername("gestion-clim-lab-c12");
    m_client->setPassword("ttn-account-v2.GzYy6dxbgfWGUqkef_7JZ6YQjjlNLdCejB4bGS58BZM");
   ...
}
Adaptation du constructeur pour permettre la connexion

Compilez et exécutez le programme, puis cliquez sur le bouton Connect :

Essayons maintenant de lire les messages. Pour cela, nous devons souscrire au Topic <AppID>/devices/<DevID>/up soit pour moi gestion-clim-lab-c12/devices/temp-hum-sensor-1/up

Renseignez le champs Topic du formulaire et cliquez sur le bouton Subscribe :

On récupère bien le message, il ne restera plus qu'à le traiter.

Notre projet

Nous allons réaliser une petite application pour afficher la température et l'humidité relative mesurée par le capteur DHT11 dans le salle C12 connecté à TTN pour la collecte des données qui sont ensuite distribuées par MQTT à tous clients connectés et qui y ont souscrit.

Le fomulaire

Créez un nouveau projet Qt nommé testMqtt et saisir le design suivant :

Le projet

Editez le fichier de définition du projet : testMqtt.pro et ajoutez y le module mqtt :

QT       += core gui
QT       += mqtt

Le code

Déclaration d'un objet de la classe QMqttClient

Editez le fichier mainwindow.h et ajoutez la librairie QMqttClient :

#include <QMqttClient>

Déclarez un objet de la classe QMqttClient :

private:
    Ui::MainWindow *ui;
    QMqttClient *m_client;

La connexion

Dans le fichier mainwindow.cpp, instanciez l'objet m_client et configurez les paramètres de connexion MQTT :

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m_client = new QMqttClient(this);
    m_client->setHostname("eu.thethings.network");
    m_client->setPort(1883);
    m_client->setUsername("gestion-clim-lab-c12");
    m_client->setPassword("ttn-account-v2.GzYy6dxbgfWGUqkef_7JZ6YQjjlNLdCejB4bGS58BZM");

}

La méthode connectToHost() permet d'établir une connexion au broker du serveur TTN. La méthode state() permet de contrôler l'état de cette connexion et le signal stateChanged() est envoyé lorsque l'état de la connexion a changé.

Nous allons donc utiliser le signal clicked() du bouton pbConnexion (dans le concepteur graphique, clique droit sur le bouton puis Aller au slot...) et connecter au slot on_pbConnexion_clicked().

Dans le constructeur, nous allons connecter le signal stateChanged() de l'objet m_client à un slot que nous appellerons stateChange() dans lequel nous testerons l'état de la connexion et nous afficherons un message en conséquence dans la barre de status de l'application.

Lorsque le status passe à Connecté, nous allons aussi souscrire aux messages relatifs aux topics qui contiennent la température et l'humidité relative :

  • gestion-clim-lab-c12/devices/temp-hum-sensor-1/up/temperature_1
  • gestion-clim-lab-c12/devices/temp-hum-sensor-1/up/relative_humidity_2
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m_client = new QMqttClient(this);
    m_client->setHostname("eu.thethings.network");
    m_client->setPort(1883);
    m_client->setUsername("gestion-clim-lab-c12");
    m_client->setPassword("ttn-account-v2.GzYy6dxbgfWGUqkef_7JZ6YQjjlNLdCejB4bGS58BZM");

    connect(m_client, &QMqttClient::stateChanged, this, &MainWindow::stateChange);
}
void MainWindow::on_pbConnexion_clicked()
{
    if (m_client->state() == QMqttClient::Disconnected) {
        ui->pbConnexion->setText(tr("Deconnexion"));
        m_client->connectToHost();

    } else {
        ui->pbConnexion->setText(tr("Connexion"));
        m_client->disconnectFromHost();
    }
}

void MainWindow::stateChange()
{
    QString message;
    switch (m_client->state())
    {
        case 0 :message = "Déconnecté";break;
        case 1 :message = "En cours de connexion";break;
        case 2 :message = "Connecté";
            QString myTopic1 = "gestion-clim-lab-c12/devices/temp-hum-sensor-1/up/temperature_1";
            auto subscription = m_client->subscribe(myTopic1);
            if (!subscription) {
                QMessageBox::critical(this, "Erreur", "Impossible de souscrire au topic\n"+myTopic1);
                return;
            }
            QString myTopic2 = "gestion-clim-lab-c12/devices/temp-hum-sensor-1/up/relative_humidity_2";
            subscription = m_client->subscribe(myTopic2);
            if (!subscription) {
                QMessageBox::critical(this, "Erreur", "Impossible de souscrire au topic\n"+myTopic2);
                return;
            }
            break;
    }

    ui->statusBar->showMessage(message);
}

Ne pas oublier de déclarer le slot en privé dans le fichier mainwindow.h :

private slots:
    void on_pbConnexion_clicked();

    void stateChange();

La réception des messages

La classe QMqttClient envoie le signal messageReceived(message, topic) lorsqu'un message MQTT arrive du broker.

Nous allons traiter ce signal dans un slot que nous appellerons receivedMessage(message, topic) dans lequel nous lirons la valeur reçue en fonction du nom du topic et nous l'afficherons dans le bon label.

De plus, nous afficherons une information furtive dans la barre de status à chaque nouvelle réception d'un message pour s'assurer de la continuité du fonctionnement. C'est important dans le cas de mesures très stables, au moins, on sait qu'on est toujours en vie !

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m_client = new QMqttClient(this);
    m_client->setHostname("eu.thethings.network");
    m_client->setPort(1883);
    m_client->setUsername("gestion-clim-lab-c12");
    m_client->setPassword("ttn-account-v2.GzYy6dxbgfWGUqkef_7JZ6YQjjlNLdCejB4bGS58BZM");

    //connect(m_client, &QMqttClient::disconnected, this, &MainWindow::brokerDisconnected);
    connect(m_client, &QMqttClient::stateChanged, this, &MainWindow::stateChange);
    connect(m_client, &QMqttClient::messageReceived, this, &MainWindow::receivedMessage);
    connect(ui->statusBar, &QStatusBar::messageChanged, this, &MainWindow::statusBarChage);

}

void MainWindow::receivedMessage(const QByteArray &message, const QMqttTopicName &topic)
{
    ui->statusBar->showMessage("Data",250);
    QString unit;
    if(topic.name()=="gestion-clim-lab-c12/devices/temp-hum-sensor-1/up/relative_humidity_2")
    {
        unit = " %";
        ui->lbHum->setText(message+unit);
    }
    if(topic.name()=="gestion-clim-lab-c12/devices/temp-hum-sensor-1/up/temperature_1")
    {
        unit = " °C";
        ui->lbTemp->setText(message+unit);
    }

}

Ne pas oublier de déclarer le slot privé receivedMessage() :

private slots:
    void on_pbConnexion_clicked();

    void brokerDisconnected();
    void stateChange();
    void receivedMessage(const QByteArray &message, const QMqttTopicName &topic);

Test

Compilez et exécuter le programme :

Télécharger les sources sur github : https://github.com/msilanus/QtMqttSimpleDHT11

Conclusion

Il est assez facile de lire les informations issues de capteurs LoRa en utilisant la classe QMqttClient. Le design de cette simple application n'est pas très développé mais vous pouvez nettement l'améliorer en utilisant par exemple les widgets de Qwt.

Télécharger les sources sur github : https://github.com/msilanus/QtMqttQwtDHT11

Et si je veux aussi envoyer des messages ...

MQTT permet aussi d'envoyer des messages et l'API MQTT de The Things Network spécifie comment faire :

Topoic : <AppID>/devices/<DevID>/down

Payload : {"port":1,"payload_raw":"Base64 message"}

Ici un autre petit projet pour piloter une LED en TOR et autre en PWM : https://github.com/msilanus/qtMqttSendReceive

 

Commentaires (0) Trackbacks (0)

Désolé, le formulaire de commentaire est fermé pour le moment

Trackbacks are disabled.