title: "MQTT Device Module — Static Tags with Alarms, Historian, and Scripting" tags: [mqtt, device-module, sparkplugb, channels, nodes, points, usertype, udt, alarms, historian, static-tags] description: "Create local UNS tags from MQTT topics via the Device Module pipeline (Channel, Node, Points). Covers flat MQTT (one tag per topic) and SparkplugB (UserTypes required). Use when you need alarms, historian, or scripting on MQTT data." version: "1.0" author: "Tatsoft"
This skill creates local persistent UNS tags mapped to MQTT topics through the Device Module pipeline: Channel, Node, Points. Unlike TagProvider (dynamic tags at runtime only), Device Module tags support alarms, historian logging, and script references.
Prerequisite: Read Skill MQTT Integration first to confirm Device Module is the right path. If you only need monitoring/visualization without alarms, use Skill MQTT TagProvider instead.
Use when:
Do NOT use when:
| Aspect | MQTT (flat) | MQTTspB (SparkplugB) |
|---|---|---|
| Protocol name | MQTT | MQTTspB |
| Interface | Custom | TCPIP |
| When to use | Generic MQTT brokers, custom topic hierarchies | SparkplugB-compliant edge nodes |
| Tag mapping | One simple tag per topic | REQUIRES UserTypes — simple tags NOT supported |
| PrimaryStation | 9 semicolon-separated fields | 15 semicolon-separated fields |
| ProtocolOptions | TimePublishRate;StoreAndForward | Type;TagProperties;TimePublishRate;StoreAndForward;PublishDBIRTHAfterNBIRTH;UseParenthesesForArray;PublishUDTsAsSimpleTags |
| Address format | MQTT topic path | SparkplugB path: GroupId/NodeId/DeviceId |
Critical rule: Always call list_protocols('mqtt') or list_protocols('MQTTspB') before writing DevicesChannels or DevicesNodes. Never guess PrimaryStation format.
Use when connecting to a standard MQTT broker publishing flat topics (not SparkplugB).
list_protocols('mqtt')
get_table_schema('DevicesChannels,DevicesNodes,DevicesPoints')
MQTT flat PrimaryStation — 9 fields: BrokerURL;Port;ClientID;UserName;Password;NetworkSecurity;X509Certificate;QoS;KeepAlive
{
"tables": [
{
"table_type": "DevicesChannels",
"data": [{
"Name": "MQTT_Channel",
"Protocol": "MQTT",
"Interface": "Custom",
"ProtocolOptions": "500;false",
"Description": "MQTT broker connection"
}]
},
{
"table_type": "DevicesNodes",
"data": [{
"Name": "Broker1",
"Channel": "MQTT_Channel",
"PrimaryStation": "localhost;1883;_SolutionName_-_Guid_;;;None;;AtLeastOnce;10",
"Description": "Local MQTT broker"
}]
}
]
}
Common PrimaryStation examples:
| Scenario | PrimaryStation |
|---|---|
| Local broker, anonymous | localhost;1883;_SolutionName_-_Guid_;;;None;;AtLeastOnce;10 |
| Remote broker | mqtt.factory.local;1883;_SolutionName_-_Guid_;;;None;;AtLeastOnce;10 |
| With authentication | broker.cloud.io;8883;MyClient;admin;pass123;TLSv.1.2;;AtLeastOnce;30 |
Tags must exist before creating DevicesPoints:
{
"table_type": "UnsTags",
"data": [
{"Name": "Plant/Line1/Temperature", "Type": "Double", "Description": "Line 1 temperature"},
{"Name": "Plant/Line1/Pressure", "Type": "Double", "Description": "Line 1 pressure"},
{"Name": "Plant/Line1/Running", "Type": "Digital", "Description": "Line 1 run status"}
]
}
Address = the MQTT topic path. The driver subscribes and updates tags automatically at runtime.
{
"table_type": "DevicesPoints",
"data": [
{"TagName": "Plant/Line1/Temperature", "Node": "Broker1", "Address": "plant/line1/temperature", "AccessType": "ReadOnly"},
{"TagName": "Plant/Line1/Pressure", "Node": "Broker1", "Address": "plant/line1/pressure", "AccessType": "ReadOnly"},
{"TagName": "Plant/Line1/Running", "Node": "Broker1", "Address": "plant/line1/running", "AccessType": "ReadWrite"}
]
}
After this, configure alarms and historian on these tags using the standard workflow (AlarmsItems, HistorianTags).
Use when connecting to a SparkplugB-compliant broker. The MQTTspB protocol does NOT support simple tags in DevicesPoints. You MUST create a UserType first, then create tags typed to that UserType, then map the main tags (not individual members) as DevicesPoints. Members auto-expand at runtime.
This is a platform constraint from the MQTTspB Collector Connector, not optional intelligence.
list_protocols('MQTTspB')
get_table_schema('UnsUserTypes,UnsTags,DevicesChannels,DevicesNodes,DevicesPoints')
Analyze the SparkplugB device structure. Map SparkplugB types to FrameworX types: double to Double, boolean to Digital, string to Text, integer to Integer.
Example using the built-in simulator (Cities/Barcelona/Panel_1 has 8 variables):
{
"table_type": "UnsUserTypes",
"data": [{
"Name": "SolarPanel",
"Description": "SparkplugB solar panel device model",
"Members": [
{"Name": "Current", "Type": "Double"},
{"Name": "Latitude", "Type": "Double"},
{"Name": "Longitude", "Type": "Double"},
{"Name": "Name", "Type": "Text"},
{"Name": "Power", "Type": "Double"},
{"Name": "State", "Type": "Digital"},
{"Name": "Temperature", "Type": "Integer"},
{"Name": "Voltage", "Type": "Double"}
]
}]
}
One tag per SparkplugB DeviceID. Each tag automatically gets all 8 members:
{
"table_type": "UnsTags",
"data": [
{"Name": "SolarFarm/Barcelona/Panel_1", "Type": "SolarPanel"},
{"Name": "SolarFarm/Barcelona/Panel_2", "Type": "SolarPanel"},
{"Name": "SolarFarm/Barcelona/Panel_3", "Type": "SolarPanel"},
{"Name": "SolarFarm/Barcelona/Panel_4", "Type": "SolarPanel"},
{"Name": "SolarFarm/Bilbao/Panel_1", "Type": "SolarPanel"},
{"Name": "SolarFarm/Bilbao/Panel_2", "Type": "SolarPanel"},
{"Name": "SolarFarm/Bilbao/Panel_3", "Type": "SolarPanel"},
{"Name": "SolarFarm/Bilbao/Panel_4", "Type": "SolarPanel"}
]
}
MQTTspB ProtocolOptions — 7 fields:Type;TagProperties;TimePublishRate;StoreAndForward;PublishDBIRTHAfterNBIRTH;UseParenthesesForArray;PublishUDTsAsSimpleTags
MQTTspB PrimaryStation — 15 fields:BrokerURL;Port;ClientID;UserName;Password;X509Certificate;CertificatePsw;NetworkSecurity;WebSocket;CollectorID;QoS;KeepAlive;ReportOutOfRangeByException;DisableRebirthRequest;PublishSTATE
{
"tables": [
{
"table_type": "DevicesChannels",
"data": [{
"Name": "SpB_Channel",
"Protocol": "MQTTspB",
"Interface": "TCPIP",
"ProtocolOptions": "Collector;;500;false;true;false;false",
"Description": "SparkplugB Collector"
}]
},
{
"table_type": "DevicesNodes",
"data": [{
"Name": "SpB_Broker",
"Channel": "SpB_Channel",
"PrimaryStation": "localhost;1883;_SolutionName_-_Guid_;;;;;None;false;;AtLeastOnce;10;false;false;true",
"Description": "Built-in broker with SparkplugB simulator"
}]
}
]
}
For MQTTspB, map only the main tag (the UserType instance). Members auto-expand at runtime. Address = GroupId/NodeId/DeviceId:
{
"table_type": "DevicesPoints",
"data": [
{"TagName": "SolarFarm/Barcelona/Panel_1", "Node": "SpB_Broker", "Address": "Cities/Barcelona/Panel_1", "AccessType": "ReadOnly"},
{"TagName": "SolarFarm/Barcelona/Panel_2", "Node": "SpB_Broker", "Address": "Cities/Barcelona/Panel_2", "AccessType": "ReadOnly"},
{"TagName": "SolarFarm/Barcelona/Panel_3", "Node": "SpB_Broker", "Address": "Cities/Barcelona/Panel_3", "AccessType": "ReadOnly"},
{"TagName": "SolarFarm/Barcelona/Panel_4", "Node": "SpB_Broker", "Address": "Cities/Barcelona/Panel_4", "AccessType": "ReadOnly"},
{"TagName": "SolarFarm/Bilbao/Panel_1", "Node": "SpB_Broker", "Address": "Cities/Bilbao/Panel_1", "AccessType": "ReadOnly"},
{"TagName": "SolarFarm/Bilbao/Panel_2", "Node": "SpB_Broker", "Address": "Cities/Bilbao/Panel_2", "AccessType": "ReadOnly"},
{"TagName": "SolarFarm/Bilbao/Panel_3", "Node": "SpB_Broker", "Address": "Cities/Bilbao/Panel_3", "AccessType": "ReadOnly"},
{"TagName": "SolarFarm/Bilbao/Panel_4", "Node": "SpB_Broker", "Address": "Cities/Bilbao/Panel_4", "AccessType": "ReadOnly"}
]
}
At runtime, Tag.SolarFarm/Barcelona/Panel_1.Current receives the live Current value from the SparkplugB metric. All 8 members of each tag update automatically.
A common production pattern combines both approaches:
The two coexist. TagProvider shows everything dynamically. Device Module tags provide the persistent infrastructure for operational logic. Example:
{
"tables": [
{
"table_type": "UnsTagProviders",
"data": [{
"Name": "SolarBroker",
"Protocol": "MQTTspB",
"PrimaryStation": "localhost;1883;_SolutionName_-_Guid_;;;;;None;false;;AtLeastOnce;10;false;false;true",
"Separators": "BranchSeparator=/;AttributeSeparator=/;",
"Description": "Dynamic browsing of all solar data"
}]
},
{
"table_type": "UnsAssetTree",
"data": [{
"Name": "SolarFarm/AllData",
"TagProviderLink": "SolarBroker",
"InitialBranch": "spBv1.0/Cities",
"Description": "Full broker namespace for navigation"
}]
}
]
}
Then add Device Module tags only for the critical points where you configure alarms (e.g., high temperature) and historian (e.g., power trending).
| Mistake | Why it happens | How to avoid |
|---|---|---|
| Using simple tags with MQTTspB DevicesPoints | Not knowing the platform constraint | MQTTspB REQUIRES UserTypes. Create UserType first, then use typed tags in Points. |
| Mapping individual UserType members as Points | Expecting per-member mapping like MQTT flat | For MQTTspB, map only the main tag. Members auto-expand at runtime. |
| Wrong Interface for MQTTspB | Assuming Custom like MQTT flat | MQTTspB uses Interface TCPIP. MQTT flat uses Custom. Check list_protocols. |
| PrimaryStation field count mismatch | MQTT has 9 fields, MQTTspB has 15 | Call list_protocols for the exact format. |
| Guessing Protocol name | Writing "SparkplugB" or "MQTT_SpB" | Exact names: MQTT and MQTTspB. Call list_protocols. |
| Creating DevicesPoints before tags | Tags are a dependency of Points | Create UnsTags first, then DevicesPoints. Multi-table write handles order. |
| Forgetting ProtocolOptions on Channel | Defaults may not match your needs | MQTTspB default: Collector;;500;false;true;false;false. MQTT default: 500;false. |
| Task | Tool call |
|---|---|
| Get MQTT flat protocol | list_protocols('mqtt') |
| Get SparkplugB protocol | list_protocols('MQTTspB') |
| Get device table schemas | get_table_schema('DevicesChannels,DevicesNodes,DevicesPoints') |
| Get UserType schema | get_table_schema('UnsUserTypes') |
| Create Channel + Node | write_objects(data='{"tables":[...]}') |
| Create UserType | write_objects('UnsUserTypes', data=[...]) |
| Create Tags | write_objects('UnsTags', data=[...]) |
| Create Points | write_objects('DevicesPoints', data=[...]) |
| Start built-in broker | designer_action('builtin_broker', 'start') |
| Start simulator | designer_action('simulator', 'start') |