MQTT Client

The MQTT client is designed in Lua and utilizes the Barracuda App Server socket API. The MQTT client enables business logic implemented in the Lua scripting language to communicate with other MQTT clients via an MQTT broker. The MQTT client can also be used for bridging MQTT clients with protocols such as HTTP, WebSockets, SMQ, etc.

Creating and operating an MQTT client is typically wrapped in a function as follows:

local function onpub(topic,msg) -- publish callback function
   trace("onpub",topic, msg) -- Print topic and payload data
end

local function connectAndRun(brokername)
   local mqttmodule = require"mqttc" -- Load MQTT Client
   local mqtt,err = mqttmodule.connect(brokername, onpub)
   if mqtt then
      mqtt:subscribe("#") -- Subscribe to all topics
      mqtt:run() -- Does not return unless the connection disconnects
   end
end

Example 1: Creating and running an MQTT client.

Code line 6 and 7 above can be optimized as follows:

local mqtt,err = require"mqttc".connect(brokername, onpub)

The MQTT client can operate in the three socket modes provided by the Barracuda App Server socket API: blocking, asynchronous, and cosocket mode. In most cases, the cosocket mode should be used. To start example 1 in cosocket mode, call function connectAndRun as follows:

ba.socket.event(function() connectAndRun(brokername) end)

Example 2: Starting example 1 in cosocket mode.

API for creating an MQTT Client

connect(addr, callback [, op])

Creates and connects an MQTT client instance:
mqtt,err=require"mqttc".connect(addr, callback [, op])

Returns mqtt-object | nil,err - function 'connect' returns an MQTT object if the connection was successful. The function returns nil,error if the connection fails. The error message can be any of the socket error messages, any of the error codes from socket:trusted() if the connection is secure, the string "invalidresp" if the client is unable to decode the response and the following MQTT broker response error codes (number):

If the connection is secure (a SharkSSL object is provided), the server's trust status is validated by calling socket:trusted() if a username or password is provided. The client will not send any credentials to the broker if the broker's certificate is not trusted. The server's trust status is not validated if no credentials are provided, thus you may connect to a non trusted server if no credentials are provided.

The following example shows how to connect using TLS, how to use credentials, and how to provide an MQTT will message:

local mqtt,err = require"mqttc".connect("mybroker.com", onpub, {
   secure=true,
   port = 23922,
   keepalive = 5*60, -- 5 minutes
   id = "my-unique-id",
   uname = "admin",
   passwd = "qwerty",
   will = {
      topic = "whoops",
      message = "Someone unplugged my cable!"
   }
})

Example 3: Connecting to a secure broker.

The broker address can be a string (name) or an already established socket connection. Using a socket connection is useful when the MQTT client requires connecting to the Internet via a proxy. The following example shows how to establish a broker socket connection via a local proxy by using the HTTP client library and the "proxycon" setting. See the HTTP client for more information on opening a proxy connection.

local function initiateMQTT()
   local http = require"httpc".create{
      proxy="localhost", -- Using local proxy
      socks=true, -- Enable SOCK5
      proxycon=true, -- Use the HTTP lib for opening a proxy connection
      proxyport=1080, -- SOCKS5 port number
   }
   -- Connect to broker 'm11.cloudmqtt.com' at port number 23922
   local ok,status = http:request{url="http://m11.cloudmqtt.com:23922"}
   if status == "prxready" then -- If connection ready
      local s = ba.socket.http2sock(http) -- Extract socket object
      -- socket is now connected to broker, but broker uses TLS.
      -- Initiate TLS handshake; use Mako's SharkSSL client if running on Mako
      ok,err=s:upgrade(ba.sharkclient())
      if ok then
         -- MQTT broker settings
         local op = {
            uname = "my-broker-username",
            passwd = "my-broker-password",
            id = "my-mqtt-id"
         }
         local function callback(topic, message)
            trace(topic,message)
         end
         local mqtt = require("mqttc").connect(s, callback, op)
         mqtt:subscribe"my-topic"
         local function run()
            trace"Starting MQTT loop"
            mqtt:run()
            trace"MQTT socket closed. Restarting..."
            ba.thread.run(initiateMQTT)
         end
         mqtt.sock:event(run, "s") -- Cosocket mode
      else
         trace("TLS upgrade failed",err)
      end
   else
      trace("Err:",status)
   end
end

-- HTTP client lib is blocking: run connection sequence in separate thread
ba.thread.run(initiateMQTT)

Example 4: Connecting via a SOCKS5 proxy.

The above example runs the MQTT loop in cosocket mode. Carefully read the sockets design tutorial prior to designing MQTT solutions.

MQTT Object Methods

mqtt:publish(topic, msg)

Publish a message.

mqtt:subscribe(topic [, callback])

Subscribe to a topic.

mqtt:unsubscribe(topic [, callback])

Unsubscribe from a topic.

mqtt:disconnect()

Gracefully disconnect from the broker.

mqtt:run()

Run the MQTT socket dispatch loop. This function does not return unless the connection disconnects. The function returns nil,error and the error message can be any of the socket error messages, the string "disconnect" if the connection was gracefully disconnected by calling mqtt:disconnect(), and the string " pingresp" if an MQTT PINGRES was not received when the client sent an MQTT PINGREQ.

The dispatcher loop is preferably called from within a cosocket, but can also be called from a dedicated thread.