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"

MQTT Device Module — Static Tags with Alarms, Historian, and Scripting

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.

When to Use This Skill

Use when:

Do NOT use when:

Prerequisites

Decision: MQTT Flat vs SparkplugB

AspectMQTT (flat)MQTTspB (SparkplugB)
Protocol nameMQTTMQTTspB
InterfaceCustomTCPIP
When to useGeneric MQTT brokers, custom topic hierarchiesSparkplugB-compliant edge nodes
Tag mappingOne simple tag per topicREQUIRES UserTypes — simple tags NOT supported
PrimaryStation9 semicolon-separated fields15 semicolon-separated fields
ProtocolOptionsTimePublishRate;StoreAndForwardType;TagProperties;TimePublishRate;StoreAndForward;PublishDBIRTHAfterNBIRTH;UseParenthesesForArray;PublishUDTsAsSimpleTags
Address formatMQTT topic pathSparkplugB path: GroupId/NodeId/DeviceId

Critical rule: Always call list_protocols('mqtt') or list_protocols('MQTTspB') before writing DevicesChannels or DevicesNodes. Never guess PrimaryStation format.

Path A: MQTT Flat — One Tag Per Topic

Use when connecting to a standard MQTT broker publishing flat topics (not SparkplugB).

Step 1: Get protocol schemas

list_protocols('mqtt')
get_table_schema('DevicesChannels,DevicesNodes,DevicesPoints')

Step 2: Create Channel + Node

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:

ScenarioPrimaryStation
Local broker, anonymouslocalhost;1883;_SolutionName_-_Guid_;;;None;;AtLeastOnce;10
Remote brokermqtt.factory.local;1883;_SolutionName_-_Guid_;;;None;;AtLeastOnce;10
With authenticationbroker.cloud.io;8883;MyClient;admin;pass123;TLSv.1.2;;AtLeastOnce;30

Step 3: Create UNS tags

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"}
  ]
}

Step 4: Map tags to topics via DevicesPoints

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).

Path B: MQTTspB (SparkplugB) — UserTypes Required

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.

Step 1: Get protocol schemas

list_protocols('MQTTspB')
get_table_schema('UnsUserTypes,UnsTags,DevicesChannels,DevicesNodes,DevicesPoints')

Step 2: Create UserType matching SparkplugB variables

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"}
    ]
  }]
}

Step 3: Create tag instances from UserType

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"}
  ]
}

Step 4: Create Channel + Node

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"
      }]
    }
  ]
}

Step 5: Map main tags as DevicesPoints

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.

Hybrid Pattern: TagProvider + Device Module

A common production pattern combines both approaches:

  1. TagProvider (Skill MQTT TagProvider) for full dynamic namespace browsing in the AssetTree
  2. Device Module (this skill) for the specific tags that need alarms, historian, or script logic

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).

Common Pitfalls

MistakeWhy it happensHow to avoid
Using simple tags with MQTTspB DevicesPointsNot knowing the platform constraintMQTTspB REQUIRES UserTypes. Create UserType first, then use typed tags in Points.
Mapping individual UserType members as PointsExpecting per-member mapping like MQTT flatFor MQTTspB, map only the main tag. Members auto-expand at runtime.
Wrong Interface for MQTTspBAssuming Custom like MQTT flatMQTTspB uses Interface TCPIP. MQTT flat uses Custom. Check list_protocols.
PrimaryStation field count mismatchMQTT has 9 fields, MQTTspB has 15Call list_protocols for the exact format.
Guessing Protocol nameWriting "SparkplugB" or "MQTT_SpB"Exact names: MQTT and MQTTspB. Call list_protocols.
Creating DevicesPoints before tagsTags are a dependency of PointsCreate UnsTags first, then DevicesPoints. Multi-table write handles order.
Forgetting ProtocolOptions on ChannelDefaults may not match your needsMQTTspB default: Collector;;500;false;true;false;false. MQTT default: 500;false.

Quick Reference — MCP Tool Calls

TaskTool call
Get MQTT flat protocollist_protocols('mqtt')
Get SparkplugB protocollist_protocols('MQTTspB')
Get device table schemasget_table_schema('DevicesChannels,DevicesNodes,DevicesPoints')
Get UserType schemaget_table_schema('UnsUserTypes')
Create Channel + Nodewrite_objects(data='{"tables":[...]}')
Create UserTypewrite_objects('UnsUserTypes', data=[...])
Create Tagswrite_objects('UnsTags', data=[...])
Create Pointswrite_objects('DevicesPoints', data=[...])
Start built-in brokerdesigner_action('builtin_broker', 'start')
Start simulatordesigner_action('simulator', 'start')

Related Skills

Documentation References