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.

MQTT client libraries have been implemented in many programming languages, for example:

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

In order to publish or subscribe to an MQTT topic from Fortran, interface bindings to a client library are required. The fortran-paho library provides a collection of ISO C binding interfaces to Eclipse Paho, and the fortran-curl to libcurl.

The examples on this page depend on an installed MQTT server. Eclipse Mosquitto should be available on most Unix-like operating systems. On FreeBSD, just install the package:

# pkg install net/mosquitto

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

# service mosquitto onestart
Starting mosquitto.

libcurl

The MQTT client implementation of libcurl has some limitations:

Install the library with developement headers. The libcurl library must have been built with MQTT support enabled. On FreeBSD, the MQTT option is disabled by default for the package in the official package repository. However, the library can be installed simply from Ports in this case.

Clone the fortran-curl repository and execute the Makefile to compile the interface bindings for Fortran:

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

Any Fortran program accessing libcurl must be linked against libfortran-curl.a -lcurl. The following example publishes a message to topic /fortran on the locally running MQTT server:

! pub_curl.f90
program main
    use, intrinsic :: iso_c_binding
    use :: curl
    implicit none

    ! The URL contains port (1883) and MQTT topic ("fortran").
    character(len=*), parameter :: URL = 'mqtt://127.0.0.1:1883/fortran'

    integer     :: rc       ! Return code.
    type(c_ptr) :: curl_ptr ! libcurl handle.

    character(len=:), allocatable, target :: payload ! Message data.

    ! Initialise libcurl and easy backend.
    rc = curl_global_init(CURL_GLOBAL_DEFAULT)
    curl_ptr = curl_easy_init()

    if (.not. c_associated(curl_ptr)) then
        stop 'Error: curl_easy_init() failed'
    end if

    ! The message to send to the MQTT topic.
    payload = 'Hello, from Fortran!'

    ! Set curl options.
    rc = curl_easy_setopt(curl_ptr, CURLOPT_URL,            URL)            ! URL of MQTT topic.
    rc = curl_easy_setopt(curl_ptr, CURLOPT_CONNECTTIMEOUT, 10)             ! Connection timeout.
    rc = curl_easy_setopt(curl_ptr, CURLOPT_NOSIGNAL,       1)              ! Skip all signal handling.
    rc = curl_easy_setopt(curl_ptr, CURLOPT_NOPROGRESS,     1)              ! Show no progress.
    rc = curl_easy_setopt(curl_ptr, CURLOPT_POST,           1)              ! Read data directly.
    rc = curl_easy_setopt(curl_ptr, CURLOPT_POSTFIELDS,     c_loc(payload)) ! Message data.
    rc = curl_easy_setopt(curl_ptr, CURLOPT_POSTFIELDSIZE,  len(payload))   ! Message size.

    ! Send request.
    rc = curl_easy_perform(curl_ptr)

    if (rc /= CURLE_OK) then
        print '("Error: ", a)', curl_easy_strerror(rc)
    else
        print '("Message published!")'
    end if

    ! Clean-up.
    call curl_easy_cleanup(curl_ptr)
    call curl_global_cleanup()
end program main

Compile and link the program:

$ gfortran13 -o pub_curl pub_curl.f90 libfortran-curl.a -lcurl

The example pub_curl connects to the MQTT server, publishes the message, and exits:

$ ./pub_curl
Message published!

Any MQTT client subscribing the topic will receive the message:

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

libpaho-mqtt3

The Paho library is available on Linux and FreeBSD. On FreeBSD, just install the package:

$ pkg install net/libpaho-mqtt3

Then, clone the fortran-paho repository and compile the 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 programs against libfortran-paho.a -lpaho-mqtt3c to access Eclipse Paho.

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 standard output:

$ mosquitto_sub -h 127.0.0.1 -t fortran

The subscribing client has to be started before the Fortran program publishes any messages, as the retained flag is not set.

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

    character(len=*), parameter :: ADDRESS   = 'tcp://127.0.0.1:1883' ! MQTT server address.
    character(len=*), parameter :: CLIENT_ID = 'FortranClient'        ! MQTT client name.
    character(len=*), parameter :: TOPIC     = 'fortran'              ! MQTT topic.
    integer,          parameter :: QOS       = 1                      ! Quality of Service (QoS).
    integer,          parameter :: TIMEOUT   = 10000                  ! Timeout in milliseconds.

    integer :: rc
    integer :: token

    character(len=:), allocatable, target :: payload
    type(c_ptr)                           :: client
    type(mqtt_client_connect_options)     :: conn_opts
    type(mqtt_client_message)             :: pub_msg

    ! The message to publish:
    payload = 'Hello, from Fortran!'

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

        ! Connect to MQTT message broker.
        conn_opts%keep_alive_interval = 20
        conn_opts%clean_session       = 1

        rc = mqtt_client_connect(client, conn_opts)

        if (rc /= MQTTCLIENT_SUCCESS) then
            print '("Failed to connect: ", i0)', rc
            exit mqtt_block
        end if

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

        ! Publish message.
        rc = mqtt_client_publish_message(client, TOPIC // c_null_char, pub_msg, token)

        print '("Waiting up to ", i0, " seconds to publish to topic ", a)', (TIMEOUT / 1000), TOPIC

        rc = mqtt_client_wait_for_completion(client, token, int(TIMEOUT, kind=c_long))
        print '("Message with token ", i0, " delivered")', token

        ! Disconnect.
        rc = mqtt_client_disconnect(client, TIMEOUT)
    end block mqtt_block

    call mqtt_client_destroy(client)
end program main

Compile the program with:

$ gfortran13 -o pub_paho pub_paho.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:

$ ./pub_paho
Waiting up to 10 seconds to publish to topic fortran
Message with 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!

Fortran Libraries

References