MQTT

MQ Telemetry Transport (MQTT) is a client-server publish/subscribe messaging protocol for resource-constrained devices in low bandwidth environments. The protocol is popular in Internet of Things applications, especially for machine-to-machine communication. An MQTT client connects to an MQTT server, also known as broker, over a network, and is then able to receive messages from and publish messages to topics.

For most programming languages, MQTT client libraries are available, for example:

Several MQTT server implementations exist that are licensed as open source:

The fortran-paho library provides a collection of ISO C binding interfaces to Eclipse Paho for publish/subscribe message passing in Fortran.

Installation

At first, we have to download and compile the Eclipse Paho C library. CMake is required to create the necessary build scripts:

$ git clone https://github.com/eclipse/paho.mqtt.c
$ cd paho.mqtt.c/
$ mkdir build && cd build/
$ cmake ..
$ make
$ make install

We can then compile the fortran-paho interface bindings:

$ git clone https://github.com/interkosmos/fortran-paho
$ cd fortran-paho/
$ make

This outputs the static library libfortran-paho.a. Link your Fortran applications with libfortran-paho.a -lpaho-mqtt3c to access Eclipse Paho.

Additionally, we have to install an MQTT server. Eclipse Mosquitto should be available on most Unix-like operating systems. On FreeBSD, simply run:

# pkg install net/mosquitto

Make sure Mosquitto is actually running on localhost before connecting any clients:

# service mosquitto onestart
Starting mosquitto.

Example

The following Fortran 2008 example publishes a plain-text message to topic fortran on the local MQTT broker instance. We can run mosquitto_sub, which is part of Mosquitto, to subscribe the topic and print incoming messages to console:

$ mosquitto_sub -h 127.0.0.1 -t fortran

The subscribing client has to be started before the Fortran program publishes any messages.

! publish.f90
program main
    use, intrinsic :: iso_c_binding
    use :: paho
    implicit none

    character(len=*), parameter :: ADDRESS   = 'tcp://localhost:1883'   ! MQTT server address.
    character(len=*), parameter :: CLIENT_ID = 'FortranPubClient'       ! MQTT client name.
    character(len=*), parameter :: TOPIC     = 'fortran'                ! MQTT topic.
    character(len=*), parameter :: TEXT      = 'Hello, from Fortran!'   ! Message text.
    integer,          parameter :: QOS       = 1                        ! Quality of Service (QoS).
    integer,          parameter :: TIMEOUT   = 10000                    ! Timeout in milliseconds.

    type(c_ptr)                       :: client
    type(mqtt_client_connect_options) :: conn_opts = MQTT_CLIENT_CONNECT_OPTIONS_INITIALIZER
    type(mqtt_client_message)         :: pub_msg   = MQTT_CLIENT_MESSAGE_INITIALIZER
    integer                           :: token
    integer                           :: rc

    ! The payload string with null-termination.
    character(len=len(TEXT) + 1, kind=c_char), target :: payload = TEXT // c_null_char

    ! Create MQTT client.
    rc = mqtt_client_create(client, &
                            ADDRESS // c_null_char, &
                            CLIENT_ID // c_null_char, &
                            MQTTCLIENT_PERSISTENCE_NONE, &
                            c_null_ptr)

    conn_opts%keep_alive_interval = 20
    conn_opts%clean_session       = 1

    ! Connect to MQTT message broker.
    rc = mqtt_client_connect(client, conn_opts)

    if (rc /= MQTTCLIENT_SUCCESS) then
        print '(a, i0)', 'Failed to connect, return code ', rc
        stop
    end if

    ! Initialise message.
    pub_msg%payload     = c_loc(payload)    ! Payload contents.
    pub_msg%payload_len = len(payload)      ! Payload size.
    pub_msg%qos         = QOS               ! Quality of Service (QoS).
    pub_msg%retained    = 0                 ! Retained flag.

    ! Publish message.
    rc = mqtt_client_publish_message(client, TOPIC // c_null_char, pub_msg, token)
    print '(a, i0, 7a)', 'Waiting for up to ', TIMEOUT / 1000, ' second(s) for publication of "', &
                         trim(payload), '" on topic "', TOPIC, '" for client with id "', &
                         CLIENT_ID, '"'

    rc = mqtt_client_wait_for_completion(client, token, int(TIMEOUT, kind=8))
    print '(a, i0, a)', 'Message with delivery token ', token, ' delivered'

    ! Disconnect.
    rc = mqtt_client_disconnect(client, TIMEOUT)
    call mqtt_client_destroy(client)
end program main

Compile the program with:

$ gfortran10 -I/usr/local/include/ -L/usr/local/lib/ \
  -o publish publish.f90 libfortran-paho.a -lpaho-mqtt3c

Once started, the example program will connect to the MQTT server and publish the message to the set topic:

$ ./publish
Waiting for up to 10 second(s) for publication of "Hello, from Fortran!" on topic "fortran"
    for client with id "FortranPubClient"
Message with delivery token 1 delivered

Any MQTT client already subscribing the topic will receive the message:

$ mosquitto_sub -h 127.0.0.1 -t fortran
Hello, from Fortran!

References