MQTT
本页面记录了 MikroTik RouterOS 中的 MQTT 集成,涵盖面向 Kaa 和 ThingsBoard 等 IoT 平台的发布者/订阅者工作流程。它解释了 MQTT 协议基础知识、RouterOS 作为 MQTT broker/client 的能力,并提供了面向 AWS/Azure 云平台的配置示例。
MQTT
本节涵盖 MQTT 集成示例。使用它可以将 RouterOS MQTT 发布和订阅工作流程连接到 Kaa IoT 和 ThingsBoard 等 IoT 平台。
MQTT 是一种开放的 OASIS 和 ISO 标准轻量级发布-订阅网络协议,用于在设备之间传输消息。典型的 MQTT 通信拓扑包括:
- 一个 MQTT publisher → 向服务器发送信息的设备。
- 一个 MQTT broker → 存储数据的服务器。
- 一个 MQTT subscriber → 读取/监控服务器上发布的数据的设备。
RouterOS 可以充当 MQTT publisher 和 subscriber(从 7.11beta2 开始)。你还可以通过 container 功能运行 MQTT broker/server。有关 Mosquitto MQTT broker 配置,请访问此链接。
你可以在下方找到 MQTT 发布场景的应用示例:
请注意,AWS 和 Azure 示例(脚本)展示的是发布 Bluetooth 标签数据。目前,只有 KNOT 内置了 Bluetooth 芯片。
配置
子菜单: /iot/mqtt
注意: 需要 iot 软件包。
IoT 软件包在 RouterOS 6.48.3 版本中可用。你可以从我们的下载页面 - 在 "Extra packages" 下获取它。
| 属性 | 描述 |
|---|---|
| brokers | 已配置的 MQTT broker 列表。 |
| connect | 指定要连接到哪个 broker 的命令。 |
| disconnect | 指定要从哪个 broker 断开连接的命令。 |
| publish | 定义需要发布的 MQTT 消息的命令。 |
| subscribe | 定义要订阅的 MQTT topic 的命令。 |
| subscriptions | 已订阅的 topic 和已接收消息的列表。 |
| unsubscribe | 指定要取消订阅哪个 topic 的命令。 |
Brokers
子菜单: /iot/mqtt/brokers
要添加新的 MQTT broker(或 MQTT 服务器),请运行以下命令:
/iot/mqtt/brokers/add
可配置属性如下所示:
| 属性 | 描述 |
|---|---|
| address (IP|hostname; 默认: ) | broker 的 IP 地址或主机名。 |
| auto-connect (yes | no; 默认: no) | 启用后,在与 MQTT broker 的连接断开/中断后,RouterOS 将反复尝试重新建立连接。 |
| certificate (string; 默认: ) | 将用于 SSL 连接的来自证书存储的证书。 |
| client-id (string; 默认: ) | 用于连接的唯一 ID。broker 使用此 ID 来识别客户端。 |
| keep-alive (integer:30..64800; 默认: 60) | 定义时间的参数(以秒为单位),在此时间之后客户端应向 MQTT broker "ping" 以表明其"存活",从而确保连接持续。此值应根据 MQTT broker 设置进行设置。 |
| name (string; 默认: ) | broker 的描述性名称。 |
| parallel-scripts-limit(integer:3..1000; 默认: off) | 定义此 broker 的 on-message 功能允许在同一时刻运行多少个脚本的参数。在持续发布大量消息的情况下,这有助于减少 CPU 占用。 |
| password (string; 默认: ) sensitive | broker 的密码(如果 broker 要求)。 |
| port (integer:0..4294967295; 默认: 1883) | broker 使用的网络端口。 |
| ssl (yes | no; 默认: no) | Secure Socket Layer 配置。 |
| username (string; 默认: ) | broker 的用户名(如果 broker 要求)。 |
| will-message (string; 默认: ) | 配置一条 Last Will and Testament(LWT)消息,该消息将在连接期间发送到 broker。此消息将存储在 broker 上。除非 RouterOS 意外断开与 broker 的连接(未发送正确的 "DISCONNECT" 数据包),否则该消息不会(由 broker)发布。 |
| will-qos(integer; 默认: ) | 配置 LWT 消息的 QoS 值。 |
| will-retain(yes | no; 默认: ) | 配置是否保留 LWT 消息。 |
| will-topic(string; 默认: ) | 配置 LWT topic,即应发布 will-message 的位置。 |
添加 broker 的示例:
/iot/mqtt/brokers/add name="broker" address="192.168.88.33" port=1883 ssl=no client-id="test-client" auto-connect=no keep-alive=60
结果:
/iot/mqtt/brokers/print
0 name="broker" address="192.168.88.33" port=1883 ssl=no client-id="test-client" auto-connect=no keep-alive=60 connected=no
Connect
子菜单: /iot/mqtt/connect
要连接到预先配置的 MQTT broker,请签发命令:
/iot/mqtt/connect broker="broker"
如果连接成功,"connected" 参数应变为 "yes":
/iot/mqtt/brokers/print
0 name="broker" address="192.168.88.33" port=1883 ssl=no client-id="test-client" auto-connect=no keep-alive=60 connected=yes
Disconnect
子菜单: /iot/mqtt/disconnect
要断开与 MQTT broker 的连接,请签发命令:
/iot/mqtt/disconnect broker="broker"
要确认 broker 已断开连接,请签发下方命令,它应指示 "connected=no":
/iot/mqtt/brokers/print
0 name="broker" address="192.168.88.33" port=1883 ssl=no client-id="test-client" auto-connect=no keep-alive=60 connected=no
Publish
子菜单: /iot/mqtt/publish
Publish 菜单用于向 MQTT broker 发送 MQTT 消息。
| 属性 | 描述 |
|---|---|
| broker (string; 默认: ) | 选择要在其中发布消息的 broker。 |
| disconnect-after (yes | no; 默认: no) | 确保在发布消息发送后自动断开与 broker 的连接的参数。 |
| force (yes | no; 默认: yes) | 如果设置为 "yes",当与 broker 的连接尚未建立("connected=no")且尝试发布消息时,RouterOS 将首先尝试与指定的 broker 建立 MQTT 连接,然后发布消息。如果设置为 "no",除非事先已建立连接("connected=yes"),否则 RouterOS 将无法发送消息。 |
| message (string; 默认: ) | 你希望发布到 broker 的消息。 |
| qos (integer:0..2; 默认: 0) | 服务质量参数,由 broker 定义。qos=0 → 消息最多被接收一次(消息被发送...发后即忘);qos=1 → 消息至少被接收一次(消息会被发送,直到发布者从 broker 收到确认数据包 <code>PUBACK</code>);qos=2 → 消息恰好被接收一次(消息被发送,并进行 4 次数据包交换以确保仅传递一次); |
| retain (yes | no; 默认: no) | 如果没有订阅该 topic 的订阅者,是保留消息还是丢弃消息。此参数由 broker 定义。保留的消息保存在 broker 上,并由 broker 自动发送给新的订阅者。 |
| topic (string; 默认: ) | topic,由 broker 定义。 |
发布消息的示例:
/iot/mqtt/publish message="test-message" broker="broker" topic="my/test/topic"
Subscribe
子菜单: /iot/mqtt/subscribe
信息
请记住,如果你与 broker 之间已有正在进行的连接(连接处于 "connected=yes" 状态),并且你通过该 broker 订阅 topic,则必须重新建立连接! 此菜单用于从 broker 订阅 MQTT topic。
| 属性 | 描述 |
|---|---|
| broker (string; 默认: ) | 选择要订阅的 broker。 |
| force (yes | no; 默认: yes) | 如果设置为 "yes",当与 broker 的连接尚未建立("connected=no")且尝试订阅时,RouterOS 将首先尝试与指定的 broker 建立 MQTT 连接,然后订阅该 topic。如果设置为 "no",除非事先已建立连接("connected=yes"),否则 RouterOS 将无法订阅该 topic。 |
| qos (integer:0..2; 默认: 0) | 服务质量参数,由 broker 定义。 |
| topic (string; 默认: ) | 要订阅的 topic,由 broker 定义。 |
订阅示例
/iot/mqtt/subscribe broker="broker" topic="my/test/topic"
也支持通配符(单级 "+" 和多级 "#")订阅(RouterOS 不允许向通配符 topic 发布,但允许订阅它们):
/iot/mqtt/subscribe broker="broker" topic="my/test/#"
/iot/mqtt/subscribe broker="broker" topic="my/test/+"
这意味着,如果你订阅 topic="my/test/#",你将能够接收发布到任何以通配符符号 "#" 之前的模式开头的 topic 的消息(例如,"my/test/topic"、"my/test/topic/something")。
而且,如果你订阅 topic="my/test/+",你将能够接收发布到 +1 级 topic 的消息(例如,"my/test/topic"、"my/test/something")。
Subscriptions
子菜单: /iot/mqtt/subscriptions
本节用于管理已添加的订阅(之前通过 Subscribe 部分添加的)。
它具有与 Subscribe 部分相同的属性。
信息
从 v7.12beta9 开始,此菜单允许你向订阅添加 "on-message" 设置。 | 属性 | 描述 | | :-- | :-- | | on-message (string; 默认: ) | 配置一个脚本,每当订阅的 topic 中接收到新消息时,该脚本将自动启动/运行。 |
要检查已订阅的 topic,请签发命令:
/iot/mqtt/subscriptions/print
0 broker=broker topic="my/test/topic" qos=0
在你如上方 Publish 部分所示发布测试消息后:
/iot/mqtt/publish message="test-message" broker="broker" topic="my/test/topic"
你应该能够在以下位置检查接收到的消息:
/iot/mqtt/subscriptions/recv/print
0 broker=broker topic="my/test/topic" data="test-message" time=2023-05-22 16:57:00
信息
接收到的消息列表限制为 1024 条,超过后,较旧的条目将被较新的条目覆盖。 要清除存储的消息,请签发命令:
/iot/mqtt/subscriptions/recv/clear
要在订阅的 topic 中出现任何新消息时运行脚本(例如,一个基本的 "log" 脚本),你可以使用 on-message 功能:
/iot/mqtt/subscriptions/set on-message={:log info "Got data {$msgData} from topic {$msgTopic}"} broker=broker 0
脚本可以使用 $msgData 和 $msgTopic 变量。$msgData 定义已发布的 MQTT 消息,$msgTopic 定义消息发布到的 MQTT topic。这两个变量在出现新消息时自动生成。
说明
- $msgData 和 $msgTopic 变量在 "System>Script" 部分创建的脚本中使用时将不起作用,也就是说,它们在添加到 "/iot/mqtt/subscriptions/set on-message={
/system/script/runx}" 的脚本中不起作用。这两个变量仅在 "on-message={}" 编写的脚本内部使用时才起作用,例如 "on-message={:log info "Got data {$msgData} from topic {$msgTopic}"}"。- 这同样适用于全局变量的使用。如果有使用其他脚本"生成"的全局变量(出现在 System>Script>Environment 部分下的变量),它们在 "on-message" 脚本内部将不起作用。
在你向订阅的 topic 发布新的 MQTT 消息后,应出现一条新的日志条目:
/log/print
10:19:15 script,info Got data {test-message} from topic {my/test/topic}
第二个示例展示了如何在出现特定消息(消息中的关键字)时运行脚本。为了实现仅在 MQTT 消息具有特定内容或关键字时运行脚本的场景,我们可以利用 if 条件语句:
/iot/mqtt/subscriptions/set 0 on-message={:if ($msgData~"\\{\"test\":\"123\"\\}") do={:log info "Got data {$msgData} from topic {$msgTopic}"}}
或者:
/iot/mqtt/subscriptions/set 0 on-message={:if ($msgData~"test") do={:log info "Got data {$msgData} from topic {$msgTopic}"}}
结果是,在每次接收到 MQTT 消息时,脚本将检查 if 条件是否为真。如果为真(如果 $msgData 包含 JSON 字符串 {"test":"123"},或者如果 $msgData 包含字符串 "test"),则将生成日志条目。否则,不会发生任何事情。
也就是说,仅当你发布如下消息时才会运行脚本:
/iot/mqtt/publish broker=broker topic="my/test/topic" message="{\"test\":\"123\"}"
当你从属于具有 on-message 配置的多个订阅的 topic 接收到消息时,只会运行 x1 个 on-message 脚本。RouterOS 将使用以下逻辑/优先级选择要运行的 on-message 脚本:
- 如果为订阅配置的 topic 完全匹配 → 第一优先级。
- 如果 topic 名称不完全匹配(使用了通配符)→ 第二优先级为单级通配符 topic。
- 如果 topic 不属于单级通配符类别 → 第三优先级为基于 topic 级别的多级通配符 topic。
示例:
/iot/mqtt/subscriptions/print
0 broker=broker topic="some/sort/of/topic" qos=0 on-message="/system/script/run script1"
1 broker=broker topic="some/#" qos=0 on-message="/system/script/run script2"
2 broker=broker topic="some/sort/of/+" qos=0 on-message="/system/script/run script3"
3 broker=broker topic="some/thing/#" qos=0 on-message="/system/script/run script4"
当你将数据发布到 some/sort/of/topic 时,将启动 script1,因为该 topic 完全匹配。
当你将数据发布到 some/sort/of/thing 时,将启动 script3 → 因为它属于单级 1v1 通配符 topic 名称。
当你将数据发布到 some/name 时,将启动 script2 → 因为它属于多级通配符 topic 名称。
当你将数据发布到 some/thing/else 时,将启动 script 4 → 因为它属于多级通配符 topic 名称(尽管它也被 some/# 通配符匹配,但它距离 some/thing/# 条目更近一级)。
Unsubscribe
子菜单: /iot/mqtt/unsubscribe
| 属性 | 描述 |
|---|---|
| broker (string; 默认: ) | 选择要取消订阅的 broker。 |
| topic (string; 默认: ) | 选择要取消订阅的 topic,由 broker 定义。 |
从 broker 和 topic 取消订阅的示例如下所示:
/iot/mqtt/unsubscribe broker="broker" topic="my/test/topic"
使用脚本发布 RouterOS 统计信息
你还可以使用脚本构造包含 RouterOS 统计信息的 MQTT 消息。然后,你可以应用调度器随时运行该脚本。
例如,你可以运行类似 mqttpublish.rsc 的脚本(将下方所示 RouterOS 代码的内容复制到新终端并按 <kbd>Enter</kbd>):
/system/script/add dont-require-permissions=no name=mqttpublish owner=admin policy=\
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\
\_Required packages: iot\r\
\n\r\
\n################################ Configuration #########################\
#######\r\
\n# Name of an existing MQTT broker that should be used for publishing\r\
\n:local broker \"broker\"\r\
\n\r\
\n# MQTT topic where the message should be published\r\
\n:local topic \"my/test/topic\"\r\
\n\r\
\n#################################### System ############################\
#######\r\
\n:put (\"[*] Gathering system info...\")\r\
\n:local cpuLoad [/system/resource/get cpu-load]\r\
\n:local freeMemory [/system/resource/get free-memory]\r\
\n:local usedMemory ([/system/resource/get total-memory] - \$freeMemory)\r\
\n:local rosVersion [/system/package/get value-name=version \\\r\
\n\A0 \A0 [/system/package/find where name ~ \"^routeros\"]]\r\
\n:local model [/system/routerboard/get value-name=model]\r\
\n:local serialNumber [/system/routerboard/get value-name=serial-number]\r\
\n:local upTime [/system/resource/get uptime]\r\
\n\r\
\n#################################### MQTT ##############################\
#######\r\
\n:local message \\\r\
\n\A0 \A0 \"{\\\"model\\\":\\\"\$model\\\",\\\r\
\n\A0 \A0 \A0 \A0 \A0 \A0 \A0 \A0 \\\"sn\\\":\\\"\$serialNumber\\\",\\\r\
\n\A0 \A0 \A0 \A0 \A0 \A0 \A0 \A0 \\\"ros\\\":\\\"\$rosVersion\\\",\\\r\
\n\A0 \A0 \A0 \A0 \A0 \A0 \A0 \A0 \\\"cpu\\\":\$cpuLoad,\\\r\
\n\A0 \A0 \A0 \A0 \A0 \A0 \A0 \A0 \\\"umem\\\":\$usedMemory,\\\r\
\n\A0 \A0 \A0 \A0 \A0 \A0 \A0 \A0 \\\"fmem\\\":\$freeMemory,\\\r\
\n\A0 \A0 \A0 \A0 \A0 \A0 \A0 \A0 \\\"uptime\\\":\\\"\$upTime\\\"}\"\r\
\n\r\
\n:log info \"\$message\";\r\
\n:put (\"[*] Total message size: \$[:len \$message] bytes\")\r\
\n:put (\"[*] Sending message to MQTT broker...\")\r\
\n/iot/mqtt/publish broker=\$broker topic=\$topic message=\$message\r\
\n:put (\"[*] Done\")"
该脚本从 RouterOS 收集数据(型号名称、序列号、RouterOS 版本、当前 CPU、已用内存、空闲内存和运行时间),并以 JSON 格式将消息(数据)发布到 broker:
/system/script/run mqttpublish
[*] Gathering system info...
[*] Total message size: 125 bytes
[*] Sending message to MQTT broker...
[*] Done
你可以订阅该 topic 以检查结果:
/iot/mqtt/subscriptions/recv/print
0 broker=broker topic="my/test/topic" data="{"model":"RB924i-2nD-BT5&BG77","sn":"E9C80EAEXXXX","ros":"7.9","cpu":13,"umem":47476736,
"fmem":19632128,"uptime":"02:21:18"}"
time=2023-05-22 17:03:52
不要忘记根据你的设置更改脚本的 "Configuration" 部分(topic 和 broker)。