MQTT in Rye

MQTT lets programs exchange messages through a broker. Publishers send messages to named topics, and subscribers receive messages from the topics they follow.

These recipes need a Rye build with MQTT support and a running broker, such as Mosquitto. They use a local broker on port 1883. Give each connected client a unique client ID.

Connect and publish

Use Open with an MQTT URI. The path after the broker address is the client ID.

client: Open mqtt://localhost:1883/rye-pub |^check "connect failed"

client .Publish-simple "sensors/temperature" "23.5"
|^check "publish failed"

client .Disconnect

Publish-simple sends a string payload with QoS 0 and no retention. Disconnect closes the connection; Close is an alias. client .Is-connected returns integer 1 when connected, otherwise 0.

If the URI has no client ID, it defaults to mqtt-client. Use explicit IDs to avoid clients disconnecting one another.

Receive messages

Run this subscriber in one terminal, then run the publisher above in another.

client: Open mqtt://localhost:1883/rye-sub |^check "connect failed"

client .Subscribe "sensors/temperature" 0 fn { payload msg } {
  print payload
  print [ "Topic:" msg."topic" ]
} |^check "subscribe failed"

print "Listening ..."
select { }             ; keep the program running

The callback receives the payload string and a metadata dictionary. The dictionary contains topic (string), qos (integer), retained and duplicate (booleans), and message-id (integer). The payload is passed separately, not stored in the dictionary.

For QoS 0, Subscribe-simple lets you omit the QoS argument:

client .Subscribe-simple "sensors/temperature" fn { payload msg } {
  print payload
} |^check "subscribe failed"

Both subscription methods now use the same callback signature and metadata fields. Compatibility note: older builds passed only metadata as the first argument to Subscribe-simple. Update those handlers to fn { payload msg } { ... }, or use Subscribe with QoS 0 when working with older builds.

Subscribe to several topics

A subscription can use + to match one topic level, or # to match all remaining levels.

; Match sensors/temperature and sensors/humidity
client .Subscribe "sensors/+" 1 fn { payload msg } {
  print [ msg."topic" payload ]
} |^check "subscribe failed"

; Later, stop receiving messages for this subscription
client .Unsubscribe "sensors/+" |^check "unsubscribe failed"

For example, sensors/# also matches sensors/room1/temperature. Wildcards belong in subscriptions, not published topic names. Unsubscribe using the same filter you subscribed with.

Choose QoS and retain the latest value

Use Publish when you want to control delivery and retention:

client: Open mqtt://localhost:1883/rye-status |^check "connect failed"

; QoS 1, retained: new subscribers can receive the latest value
client .Publish "sensors/temperature" "23.5" 1 1
|^check "publish failed"

client .Disconnect

QoS can be 0 (at most once), 1 (at least once), or 2 (exactly once at the MQTT protocol level). Both publishing and subscribing accept a QoS level; a higher subscription QoS does not upgrade a QoS 0 publication.

The last argument is an integer retain flag: use 0 for ordinary messages and 1 to retain the latest message on that topic. To clear a retained value, publish an empty payload with retention enabled:

client .Publish "sensors/temperature" "" 1 1

This last snippet assumes an open client connection.

Authentication and TLS

URI connections accept mqtt://[user:password@]host:port/client-id. Use mqtts:// for TLS:

; Replace this address with your TLS-enabled broker
client: Open mqtts://broker.example:8883/rye-secure
|^check "TLS connection failed"

Credentials and client IDs in URIs use URL percent-encoding for reserved characters. A client ID occupies a single path segment. Query strings and fragments are not supported.

For explicit configuration, use mqtt-options. Its setters return the same options object, so they can be chained:

opts: mqtt-options
|Set-broker "ssl://broker.example:8883"
|Set-client-id "rye-secure"
|Set-username "user"
|Set-password "replace-with-your-password"
|Set-keep-alive 60

client: opts |Connect-with-options |^check "connect failed"
client .Publish-simple "app/heartbeat" "ok" |^check "publish failed"
client .Disconnect

Set-broker takes a string with the transport scheme tcp:// or ssl://, unlike the Rye URI schemes mqtt:// and mqtts://. Keep-alive is in seconds. Configure options before connecting.

Use TLS when sending credentials over a network. Keep real passwords out of source files. These bindings do not expose custom CA or client-certificate configuration; TLS uses the underlying client’s default certificate verification.

Report unexpected disconnections with a last will

A last will lets the broker announce that a client disappeared unexpectedly. Configure it before connecting, then publish an online status:

opts: mqtt-options
|Set-broker "tcp://localhost:1883"
|Set-client-id "rye-device"
|Set-keep-alive 30
|Set-will "devices/rye-device/status" "offline" 1 1

client: opts |Connect-with-options |^check "connect failed"
client .Publish "devices/rye-device/status" "online" 1 1
|^check "publish failed"

; Do the device's work here.

; On a planned shutdown, publish the status ourselves.
client .Publish "devices/rye-device/status" "offline" 1 1
|^check "publish failed"
client .Disconnect

Set-will takes a topic, string payload, QoS, and integer retain flag. Here both statuses are retained so new subscribers can see the latest status.

A normal Disconnect does not trigger the will. On an unexpected loss of connection, the broker publishes it after detecting the loss.

Troubleshooting

  • Check the broker address, port, and firewall if connecting fails.
  • Use distinct client IDs for simultaneously connected programs.
  • Keep subscribers running: a script that exits immediately after subscribing cannot keep receiving messages.
  • Use |^check "message" to add context and return on a failed operation.
  • Public test brokers are shared and may disconnect clients. Do not publish sensitive data to them.

See also

  • CLI dialect for adding command-line options to a publisher or subscriber.
  • Rye examples: examples/mqtt/publisher.rye and examples/mqtt/subscriber.rye.
  • MQTT and Mosquitto.