Integrating MQTT into a Laravel project

Photo by S. Tsuchiya on Unsplash

Integrating MQTT into a Laravel project

Recently, I have been experimenting with MQTT, a lightweight messaging protocol that is designed to provide efficient, reliable, and real-time communication between devices and applications in machine-to-machine (M2M) and Internet of Things (IoT) contexts. It's a popular communication protocol used by IoT devices. For the project I am working on, I plan to send data from my device to an MQTT broker and have a web application subscribe to a topic to get data the device is publishing.

I installed Mosquitto, a popular MQTT broker and MQTTX, an easy-to-use GUI MQTT client. I found a PHP library called PHP-MQTT that makes integrating MQTT into your Laravel project easy. The code I used to get data from the MQTT broker is quite simple.

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use PhpMqtt\Client\Facades\MQTT;

class SubscribeToTopic extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'mqtt:subscribe';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Subscribe To MQTT topic';

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $mqtt = MQTT::connection();
        $mqtt->subscribe('devices/+/status', function(string $topic, string $message) {
            echo sprintf('Received message on topic [%s]: %s',$topic, $message);
        });

        $mqtt->loop(true);
        return Command::SUCCESS;
    }
}

Publishing data to the MQTT Topic

Data received by laravel application

The code worked well locally, but I started thinking about how to run the command when deploying the project to production. My initial idea was to create a command and run it with Laravel's scheduler, but this approach did not work well.

The issue is that the command runs an infinite loop, causing the function to never return. Consequently, the scheduler creates multiple processes, all subscribed to the same topic. The code that runs when new data is received (e.g., storing the data in a database) will be executed multiple times, with the exact number depending on the number of processes created. To solve this problem, you can use a process monitor, such as supervisor, to run the command in the background and restart it if it crashes. I copied the configuration example for running the queue worker command from the Laravel documentation and edited it as follows:

[program:laravel-mqtt-subscriber]
process_name=%(program_name)s_%(process_num)02d
command=php /home/forge/app.com/artisan mqtt:subscribe
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=forge
numprocs=1
redirect_stderr=true
stdout_logfile=/home/forge/app.com/worker.log
stopwaitsecs=3600

The main point to note is that I changed the value of numprocs to 1 to prevent multiple processes from running the same command, which resolved the issue.