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:
- Eclipse Paho (C, C++, Java, JavaScript, Python, Go, …)
- libcurl (C)
- MQTT-C (C)
- luamqtt (Lua)
- PubSubClient (Arduino)
Several MQTT server implementations exist that are licenced as open source:
- Eclipse Mosquitto (Linux, Unix, macOS, Microsoft Windows)
- Apache ActiveMQ (Java)
- HMBQTT (Python)
- VerneMQ (Linux)
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:
- Only QoS level 0 is implemented for publish.
- No way to set retain flag for publish.
- No TLS (mqtts) support.
- Naive EAGAIN handling will not handle split messages.
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
- fortran-curl: Fortran 2008 interface bindings to libcurl
- fortran-paho: Fortran 2008 interface bindings to the Eclipse Paho MQTT client library
References
- Eclipse Paho: MQTT client library
- Eclipse Mosquitto: MQTT server/broker
- libcurl: Multiprotocol file transfer library
< ZeroMQ | [Index] | UNIX System V Message Queues > |