luamqtt

repository·master·Indexed 41 days ago

https://github.com/xhaskx/luamqtt

A pure-Lua MQTT client library supporting MQTT v3.1.1 and v5.0 protocols. It is designed for portability across Lua environments and includes built-in connectors for luasocket, luasocket_ssl, and OpenResty (ngxsocket). The library supports event-driven workflows via an ioloop, Last Will & Testament (LWT) functionality, and custom connector implementations.

Tokens
4.4K
Snippets
15
Records
21
Agent score
51%

What's inside luamqtt

  1. How connectors work in luamqtt

    master

    A connector is the network connection layer responsible for establishing a network stream (typically TCP) and handling data transmission (send/receive).

    luamqtt includes three standard connectors:

    1. luasocket (mqtt/luasocket.lua)
    2. luasocket_ssl (mqtt/luasocket_ssl.lua)
    3. ngxsocket (mqtt/ngxsocket.lua) — specifically for use in OpenResty environments.

    Default Behavior

    The library automatically selects a connector based on the secure option in the client configuration:

    • If secure = true, it uses luasocket_ssl.
    • If secure = false, it uses luasocket.

    Custom Connectors

    You can implement your own connector to make luamqtt work in specialized environments. A connector is simply a set of functions that manage the network stream. Each MQTT client instance can have its own unique connector.

  2. Install luamqtt and its dependencies

    master

    To use luamqtt, you must first install its required dependencies via LuaRocks. Note that some dependencies are optional depending on your Lua version and whether you require SSL/TLS.

    Required Dependencies

    • luasocket: Required for TCP connections. (Note: If you provide your own connector, this is optional).
    • luabitop: Required only if you are using Lua 5.1. If you are using Lua 5.3 or above, you do not need this.

    Optional Dependencies

    • luasec: Required only if you need to establish secure (SSL/TLS) connections to an MQTT broker.

    Installation Steps

    1. Install dependencies:
    luarocks install luasocket
    luarocks install luabitop  # Only for Lua 5.1
    luarocks install luasec    # Only for SSL/TLS
    1. Install the library:
    luarocks install luamqtt

    For development, you can install the development branch or build from a cloned repository:

    # Install development branch
    luarocks install luamqtt --dev
    
    # Build from cloned repo
    luarocks make
    luarocks install luasocket
    luarocks install luabitop
    luarocks install luasec
    luarocks install luamqtt
  3. Build a luamqtt package for OpenWRT

    master

    Since luamqtt is written in pure Lua, you do not need to perform a standard compilation process. Instead, you only need to ensure the files are packed correctly for the OpenWRT environment.

    You can use the provided bash script to package the library without requiring the full OpenWRT source tree:

    ./make-package-without-openwrt-sources.sh
  4. Run luamqtt in OpenResty synchronous mode

    master

    In synchronous mode, the MQTT client connects, subscribes, and waits for incoming publications linearly as written in the code, without asynchronous magic.

    Important Caveat: The keep_alive feature is not functional in this mode. Because the client cannot interrupt its receive() operation to send PINGREQ packets, the MQTT broker may disconnect the client if there is no incoming traffic. To handle disconnects, you should implement a reconnection strategy using OpenResty's timer.

    app/main-sync.lua
  5. Run luamqtt in OpenResty ioloop mode

    master

    In ioloop mode, the MQTT client connects, subscribes, and waits for publications while actively maintaining the connection using PINGREQ packets at the configured keep_alive interval.

    Important Caveat: This mode relies on socket timeouts to allow the client to wake up and send PINGREQ packets. Consequently, OpenResty will log errors in the error.log every time a timeout occurs, appearing as:

    stream lua tcp socket read timed out, context: ngx.timer

    app/main-ioloop.lua
  6. Explore advanced MQTT examples and tests

    master

    For more complex usage patterns beyond the basic workflow, you can examine the following resources in the repository:

    • Last Will and Testament: See the examples/last-will/ directory for implementations of MQTT Last Will functionality.
    • Client Implementation Details: The tests/spec/mqtt-client.lua file contains MQTT client tests which serve as a comprehensive reference for how the client is intended to be used and tested.
  7. Parse connection URIs

    master

    The client parses the uri argument to determine the host and port.

    • Format: host:port (e.g., 127.0.0.1:1883).
    • Host only: If no port is provided (e.g., localhost), the client defaults to:
      • 1883 for standard connections.
      • 8883 if secure is enabled.
    • Error: If the URI does not contain at least a host/IP, an error is raised.
  8. Initialize an MQTT client instance

    master

    Create an MQTT client by calling the constructor with an args table. The client supports both MQTT v3.1.1 (version 4) and MQTT v5.0 (version 5).

    local mqtt = require("mqtt")
    
    local client = mqtt.client({
        uri = "localhost:1883",
        clean = true,
        version = 4, -- or 5 for MQTT v5.0
        id = "my-client-id",
        username = "user",
        password = "pass",
        secure = true, -- uses luasec for SSL/TLS
        keep_alive = 60,
        reconnect = true -- or a number for timeout in seconds
    })
  9. Basic MQTT workflow with simple.lua

    master

    The examples/simple.lua script demonstrates the fundamental lifecycle of an MQTT client using luamqtt. It covers the following sequence of operations:

    1. Connecting: Establishing a connection to an MQTT broker.
    2. Subscribing: Subscribing to a specific topic.
    3. Publishing: Sending a message to a topic after the subscription is created.
    4. Receiving: Handling the incoming message (in this case, the self-published message).
    5. Disconnecting: Gracefully closing the connection to the broker.

    Expected console output follows this pattern:

    created MQTT client     mqtt.client{id="luamqtt-v2-0-0-5807dc7"}
    running ioloop for it
    connected:      CONNACK{rc=0, sp=false, type=2}
    subscribed:     SUBACK{rc={1}, packet_id=1, type=9}
    publishing test message "hello" to "luamqtt/simpletest" topic...
    received:       PUBLISH{payload="hello", topic="luamqtt/simpletest", dup=false, retain=false, qos=1, packet_id=1, type=3}
    disconnecting...
    done, ioloop is stopped
    -- Refer to examples/simple.lua for the full implementation
  10. Create an MQTT client and handle events

    master

    You can create an MQTT client using mqtt.client and define event handlers using the :on method. Common events include connect (triggered when the connection is established) and message (triggered when a message is received on a subscribed topic).

    To keep the client running and processing events, you must call mqtt.run_ioloop(client).

    Example: Connect, Subscribe, and Publish

    local mqtt = require("mqtt")
    
    -- Create client
    local client = mqtt.client{
        uri = "mqtt.flespi.io", 
        username = os.getenv("FLESPI_TOKEN"), 
        clean = true
    }
    
    -- Assign event handlers
    client:on{
        connect = function(connack)
            if connack.rc ~= 0 then
                print("connection to broker failed:", connack:reason_string(), connack)
                return
            end
    
            -- Subscribe to a topic upon connection
            assert(client:subscribe{
                topic="luamqtt/#", 
                qos=1, 
                callback=function()
                    assert(client:publish{ topic = "luamqtt/simpletest", payload = "hello" })
                end
            })
        end,
    
        message = function(msg)
            assert(client:acknowledge(msg))
            print("received message", msg)
            client:disconnect()
        end,
    }
    
    -- Start the event loop
    mqtt.run_ioloop(client)
    local mqtt = require("mqtt")
    
    local client = mqtt.client{ uri = "mqtt.flespi.io", username = os.getenv("FLESPI_TOKEN"), clean = true }
    
    client:on{
        connect = function(connack)
            if connack.rc ~= 0 then
                print("connection to broker failed:", connack:reason_string(), connack)
                return
            end
    
            assert(client:subscribe{ topic="luamqtt/#", qos=1, callback=function()
                assert(client:publish{ topic = "luamqtt/simpletest", payload = "hello" })
            end})
        end,
    
        message = function(msg)
            assert(client:acknowledge(msg))
            print("received message", msg)
            client:disconnect()
        end,
    }
    
    mqtt.run_ioloop(client)
  11. Simulate a Last Will & Testament (LWT) event

    master

    The Last Will & Testament (LWT) feature allows a client to specify a message that the broker will automatically publish to a specific topic if the client's connection is lost unexpectedly (e.g., without sending a DISCONNECT packet).

    To simulate this behavior:

    1. Configure the LWT: When initializing the client connection, specify the LWT topic and payload.
    2. Trigger a connection loss: Instead of calling a graceful disconnect method, terminate the network connection abruptly (simulating a crash or network failure).
    3. Verify delivery: A second client subscribed to the LWT topic will receive the message published by the broker on behalf of the disconnected client.
    -- Conceptual workflow based on the example
    -- Client 1: Connects with LWT configured, then loses connection abruptly
    -- Client 2: Subscribes to the LWT topic and waits for the broker to deliver the message
  12. Configure MQTT client connection arguments

    master

    When initializing the client, you can provide the following keys in the args table:

    KeyTypeDescription
    uristringRequired. Broker URI in host:port or host format.
    cleanbooleanRequired. Clean session start flag.
    versionnumberProtocol version: 4 (v3.1.1) or 5 (v5.0). Defaults to 4.
    idstringClient ID. Generated automatically if absent.
    usernamestringUsername for broker authorization.
    passwordstringPassword for broker authorization. Requires username.
    secureboolean or tableIf true, uses default SSL params. If table, provide luasec-compatible options (e.g., cafile, certificate).
    willtableWill message: { topic="...", payload="...", qos=1, retain=true }.
    keep_alivenumberPINGREQ interval in seconds. Defaults to 60.
    reconnectboolean or numberIf true, reconnects on close. If number, provides reconnect timeout in seconds.
    connectortableCustom connector table. Defaults to mqtt.luasocket or mqtt.luasocket_ssl.
    ssl_modulestringName of the SSL module to use. Defaults to "ssl".