php-mqtt / client

An MQTT client written in and for PHP.
MIT License
372 stars 72 forks source link

The broker responded with unauthorized. #99

Closed Natoons closed 2 years ago

Natoons commented 2 years ago

Hello everyone, I used the code to subscribe on the mqtt of ttn with this EXEMPLE CODE. I have configured everything. Just need to insert the topic. I don't know how to do it. It is surely the lack of topic which poses problem MQTT [eu1.cloud.thethings.network:1883] [test-subscriber] The broker responded with unauthorized. Subscribing to a topic using QoS 0 failed. An exception occurred.

Namoshek commented 2 years ago

Please provide the code you used to TTN.

Natoons commented 2 years ago

I took the example : 01_subscribe_with_qos_0.php I put the values in shared/config.php.

Namoshek commented 2 years ago

Then I guess you did not provide credentials via the ConnectionSettings. I'm not familiar with the authentication system of TTN, but it clearly sounds like you need to provide credentials.

Natoons commented 2 years ago

I managed to put it all together in shared/config.php . But I miss to integrate the topic

Connection information :

MQTT server host :

Public address : 
Public TLS address : 

Connection credentials :

Username : 
Password :
Topic : 

I did not understand the use : ConnectionSettings.php What should I put in it

Namoshek commented 2 years ago

The use of ConnectionSettings is explained in the README. Depending on the example you based your code on, you essentially need to swap a few lines only:

// Create a new instance of an MQTT client and configure it to use the shared broker host and port.
$client = new MqttClient(MQTT_BROKER_HOST, MQTT_BROKER_PORT, 'test-publisher', MqttClient::MQTT_3_1, null, $logger);

// Create and configure the connection settings.
$connectionSettings = (new \PhpMqtt\Client\ConnectionSettings)
    ->setUsername(USERNAME)
    ->setPassword(PASSWORD);

// Connect to the broker without specific connection settings but with a clean session.
$client->connect($connectionSettings, true);
Natoons commented 2 years ago

Hello, I found it and it works

<?php

declare(strict_types=1);

require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../shared/config.php';

use PhpMqtt\Client\ConnectionSettings;
use PhpMqtt\Client\Examples\Shared\SimpleLogger;
use PhpMqtt\Client\Exceptions\MqttClientException;
use PhpMqtt\Client\MqttClient;
use Psr\Log\LogLevel;

// Créez une instance d'un enregistreur conforme à la norme PSR-3. Pour cet exemple, nous utiliserons également le logger pour enregistrer les exceptions.
$logger = new SimpleLogger(LogLevel::INFO);

try {
    // Créez une nouvelle instance d'un client MQTT et configurez-la pour utiliser l'hôte et le port du courtier partagé.
    $client = new MqttClient(MQTT_BROKER_HOST, MQTT_BROKER_PORT, 'test-publisher', MqttClient::MQTT_3_1, null, $logger);

    // Créez et configurez les paramètres de connexion selon les besoins.
    $connectionSettings = (new ConnectionSettings)
        ->setUsername(AUTHORIZATION_USERNAME)
        ->setPassword(AUTHORIZATION_PASSWORD);

    // Connectez-vous au courtier avec les paramètres de connexion configurés et avec une session propre.
    $client->connect($connectionSettings, true);

    // S'abonner au sujet 'v3/@ttn/devices/eui-a86/up' en utilisant QoS 0.

    $client->subscribe('v3/@ttn/devices/eui-a86/up', function (string $topic, string $message, bool $retained) use ($logger, $client) {
        $logger->info('Nous avons reçu un {typeOfMessage} sur le topic [{topic}]: {message}', [
            'topic' => $topic,
            'message' => $message,
            'typeOfMessage' => $retained ? 'retained message' : 'message',

        ]);
        // $msg =json_decode($message);
        json_decode($message);
        // $frm_payload = $msg->frm_payload;
        // Après avoir reçu le premier message sur le sujet souscrit, nous voulons que le client cesse d'écouter les messages.
        $client->interrupt();
    }, MqttClient::QOS_AT_MOST_ONCE);

    // Puisque la souscription nécessite d'attendre les messages, nous devons lancer la boucle client qui s'occupe de la réception,
    // de l'analyse et de la livraison des messages aux callbacks enregistrés. La boucle s'exécutera indéfiniment, jusqu'à ce qu'un message
    // soit reçu, ce qui interrompra la boucle.
    $client->loop(true);

    // Termine gracieusement la connexion au courtier.
    $client->disconnect();
} catch (MqttClientException $e) {
    // MqttClientException est l'exception de base de toutes les exceptions de la bibliothèque. Si vous l'attrapez, vous attraperez toutes les exceptions liées à MQTT.
    $logger->error('Connecting with username and password or publishing with QoS 0 failed. An exception occurred.', ['exception' => $e]);
} 

But I would like to decode the message and take a part

I made this :

        $msg =json_decode($message);
        $frm_payload = $msg->frm_payload;
Namoshek commented 2 years ago

Glad you found out how it works!

In the $client->subscribe($topic, $callback) callback function, you can use any code you like and need. This library only solves the transmission of data, not the encoding/decoding of the transferred data. I cannot help you with encoding/decoding of your data.

Natoons commented 2 years ago

I managed to parse and decode, I would like to know if you have an idea to display all uploads when a person joins the php page

Do I have to change something here : $client->interrupt(); Because currently we see only one message but I would like to see more

Namoshek commented 2 years ago

You are touching a difficult topic here since MQTT subscriptions are meant to be long running processes. Performing such a subscription while processing a normal HTTP request is not going to end very well, especially not in regards to response times. What you'd want to use instead is a long running CLI script which stores data received via a subscription in a database or similar. This data can then be fetched and displayed to users from another PHP process.

If you really want to retrieve all data during processing of an HTTP request, you'd either need the publisher to use the retain flag (which makes the MQTT broker store the values) or have all publishers send their data at once, which doesn't scale. You'd also need to know the number of publishers for a better interrupt logic (i.e. count the received messages and interrupt when all publishers sent their data). An additional timeout for the subscription would be quite useful as well. Keep in mind that most users expect a website to be loaded in less than 3 seconds (and by Google's standards, this is already an eternity).

Natoons commented 2 years ago

I normally receive a message from TTN MQTT every two minutes. Currently I always have to refresh the page to do so. Is it possible to make an algorithm if the message received, refresh the page in php? image I have a log file, and also I need to put the values in a database for my friend to retrieve for his mobile application. Thank you in any case for your explanations

Namoshek commented 2 years ago

From your explanation it is clear that you need two separate pieces of software:

  1. The CLI script, which runs infinitely (or until manually stopped). It subscribes to relevant topics and stores received data after decoding in a database.
  2. The web script, which retrieves data from the database and displays it to the user. Whether this displays all data or only the most recent x entries is totally up to you.

Given these two scripts, you for sure can use some JavaScript in the frontend to refresh the page every x seconds. Forcing a refresh from the server is not possible. You could also use long polling but this is rather difficult to implement with PHP and blocks entire php-fpm workers. A definitely more complex but also more modern solution would involve using a real-time messaging service for the frontend, like Pusher (or a compatible, self-hosted server like soketi/soketi). This would basically eliminate the need for the web script - a static website with some JavaScript would be enough as frontend.