UNS (Unified Namespace) Connector
Overview
Section titled “Overview”The UNS (Unified Namespace) connector is a processor that constructs an ISA-95-aligned topic hierarchy from the connector configuration and the incoming payload, then emits either a JSON payload with an injected _uns_topic key or a SparkplugB-style envelope wrapping the data in a metrics array. It is the canonical building block for organizing plant data into a Unified Namespace as popularized by Walker Reynolds and the ISA-95 standard.
Connector Types:
UNS- Stateless processor that decorates payloads with an ISA-95 topic and reshapes the output as JSON or SparkplugB
Features
Section titled “Features”- ✅ ISA-95 hierarchy: Enterprise / Site / Area / Line / Cell
- ✅ Optional payload-driven dynamic topic levels (mappings)
- ✅ Two output formats:
json(passthrough +_uns_topic) andsparkplugb(envelope with metrics array) - ✅ Skips empty hierarchy levels gracefully
- ✅ Per-message timestamp (Unix milliseconds) on SparkplugB output
Basic Configuration
Section titled “Basic Configuration”Static ISA-95 Hierarchy with JSON Output
Section titled “Static ISA-95 Hierarchy with JSON Output”{ "type": "UNS", "config": { "enterprise": "Acme", "site": "Milan", "area": "Assembly", "line": "Line1", "cell": "Station3", "outputFormat": "json" }}Dynamic Topic Levels from Payload
Section titled “Dynamic Topic Levels from Payload”{ "type": "UNS", "config": { "enterprise": "Acme", "site": "Milan", "area": "Assembly", "mappings": [ { "inputKey": "machine_id", "topicLevel": "machine" }, { "inputKey": "shift_id", "topicLevel": "shift" } ], "outputFormat": "json" }}SparkplugB Envelope Output
Section titled “SparkplugB Envelope Output”{ "type": "UNS", "config": { "enterprise": "Acme", "site": "Milan", "area": "Packaging", "line": "Line2", "outputFormat": "sparkplugb" }}Configuration Parameters
Section titled “Configuration Parameters”Enterprise
Section titled “Enterprise”Required. The top-level ISA-95 enterprise (company/division name).
{ "enterprise": "Acme" }Required. The physical site (plant, factory, building).
{ "site": "Milan" }Optional. A functional area within the site.
{ "area": "Assembly" }Optional. A production line within the area.
{ "line": "Line1" }Optional. A specific cell or workstation within the line.
{ "cell": "Station3" }Mappings
Section titled “Mappings”Optional. An array of payload-to-topic-level mappings. For each mapping, the connector looks up data[inputKey] and appends <topicLevel>/<value> to the topic.
{ "mappings": [ { "inputKey": "machine_id", "topicLevel": "machine" }, { "inputKey": "shift_id", "topicLevel": "shift" } ]}Behavior:
- Both
inputKeyandtopicLevelare required for each entry - Missing input keys are reported via the error channel; the topic level is omitted
- Non-string input values are coerced with
fmt.Sprintf("%v", ...)
Output Format
Section titled “Output Format”Required. Must be one of:
json- Passthrough of the original payload with_uns_topicinjectedsparkplugb- Envelope{ topic, timestamp, metrics }where metrics is a list of{ name, value }pairs
{ "outputFormat": "sparkplugb" }Topic Construction
Section titled “Topic Construction”Topic levels are joined with /. Empty optional levels (Area, Line, Cell) are skipped. Mappings appear in the order they’re declared.
Example:
Configuration:
{ "enterprise": "Acme", "site": "Milan", "area": "Assembly", "line": "Line1", "mappings": [ { "inputKey": "machine_id", "topicLevel": "machine" } ]}Incoming payload:
{ "machine_id": "M-007", "temperature": 24.5 }Resulting topic:
Acme/Milan/Assembly/Line1/machine/M-007Output Payload Examples
Section titled “Output Payload Examples”JSON Output
Section titled “JSON Output”Input:
{ "machine_id": "M-007", "temperature": 24.5, "pressure": 5.2}Configuration:
{ "enterprise": "Acme", "site": "Milan", "area": "Assembly", "line": "Line1", "mappings": [ { "inputKey": "machine_id", "topicLevel": "machine" } ], "outputFormat": "json"}Output:
{ "machine_id": "M-007", "temperature": 24.5, "pressure": 5.2, "_uns_topic": "Acme/Milan/Assembly/Line1/machine/M-007"}SparkplugB Envelope Output
Section titled “SparkplugB Envelope Output”Same input and base configuration with outputFormat: sparkplugb:
{ "topic": "Acme/Milan/Assembly/Line1/machine/M-007", "timestamp": 1747740875123, "metrics": [ { "name": "machine_id", "value": "M-007" }, { "name": "temperature", "value": 24.5 }, { "name": "pressure", "value": 5.2 } ]}The timestamp is Unix milliseconds at processing time. Note that the order of metrics entries is not guaranteed (Go map iteration is unordered).
Data Flow
Section titled “Data Flow”DataPayload → UNS → DataPayload + _uns_topic (json) OR { topic, timestamp, metrics } (sparkplugb)Common Use Cases
Section titled “Common Use Cases”1. Bridging PLC Data to MQTT with ISA-95 Topic
Section titled “1. Bridging PLC Data to MQTT with ISA-95 Topic”Reshape raw PLC payloads into a UNS-compliant MQTT topic:
{ "type": "UNS", "config": { "enterprise": "Acme", "site": "Detroit", "area": "Stamping", "line": "PressLine1", "outputFormat": "json" }}Downstream MqttV5Writer then uses _uns_topic as the publish topic via a Reshape step that maps _uns_topic → MQTT topic.
2. SparkplugB Publishing for Ignition or HiveMQ
Section titled “2. SparkplugB Publishing for Ignition or HiveMQ”Many SCADA platforms (Ignition, HiveMQ, etc.) expect SparkplugB envelopes. Use sparkplugb output to match:
{ "type": "UNS", "config": { "enterprise": "Acme", "site": "Milan", "area": "Bottling", "line": "Filler1", "outputFormat": "sparkplugb" }}3. Multi-Tenant Topic Routing by Customer
Section titled “3. Multi-Tenant Topic Routing by Customer”Use dynamic mappings to embed the customer identifier in the topic:
{ "type": "UNS", "config": { "enterprise": "ConnectCo", "site": "Cloud", "mappings": [ { "inputKey": "tenant_id", "topicLevel": "tenant" }, { "inputKey": "site_id", "topicLevel": "customer-site" }, { "inputKey": "device_id", "topicLevel": "device" } ], "outputFormat": "json" }}Resulting topic for {tenant_id: "T-1", site_id: "S-99", device_id: "D-42"}:
ConnectCo/Cloud/tenant/T-1/customer-site/S-99/device/D-42Troubleshooting
Section titled “Troubleshooting”Missing _uns_topic in Output
Section titled “Missing _uns_topic in Output”Problem: Downstream consumer does not see the _uns_topic key
Solutions:
- Confirm
outputFormat: jsonis set (the SparkplugB envelope replaces the payload entirely; it hastopicat the top level instead of_uns_topic) - Verify the UNS connector is actually in the pipeline — use a
Logwriter to dump intermediate payloads
input key %q not found in payload Errors
Section titled “input key %q not found in payload Errors”Problem: A configured mapping references a key that isn’t present in the payload
Solutions:
- Verify the input key matches the upstream payload exactly (case-sensitive)
- Use a
ReshapeorTransformupstream to ensure required keys exist - If the key is sometimes missing, consider making it part of static
area/line/cellinstead
SparkplugB Output Has Empty metrics Array
Section titled “SparkplugB Output Has Empty metrics Array”Problem: Output envelope is { topic, timestamp, metrics: [] }
Solutions:
- The metrics array is built from the keys of the input payload. An empty input → empty metrics
- Verify the upstream connector is delivering non-empty payloads
Topic Has Unexpected Slashes or Empty Segments
Section titled “Topic Has Unexpected Slashes or Empty Segments”Problem: Topic contains // or trailing slashes
Solutions:
- Don’t include slashes in
enterprise,site, etc. — the connector inserts them. Embedded slashes will produce malformed paths - Avoid empty strings for optional levels — leave the field unset rather than passing
""
Sorting Concerns for SparkplugB Metrics
Section titled “Sorting Concerns for SparkplugB Metrics”Problem: Metrics ordering is inconsistent between runs
Solutions:
- Go map iteration is intentionally unordered. If downstream consumers care about order, sort upstream via a
Reshapethat converts the metrics into an explicit list before reaching this connector - Or have the consumer sort by
nameon receipt
Best Practices
Section titled “Best Practices”1. Treat the UNS Topic as a Contract
Section titled “1. Treat the UNS Topic as a Contract”The topic hierarchy is consumed by dashboards, historians, and other Meddle pipelines. Once published, treat it as a stable interface — renaming area or line after the fact breaks downstream consumers.
2. Use Static Levels for Stable Hierarchies
Section titled “2. Use Static Levels for Stable Hierarchies”Prefer static enterprise/site/area/line/cell over mappings whenever the hierarchy doesn’t depend on the payload. Mappings are powerful but add a dependency on payload shape.
3. Prefer JSON for Greenfield, SparkplugB for SCADA Bridging
Section titled “3. Prefer JSON for Greenfield, SparkplugB for SCADA Bridging”json is simpler and works with any downstream consumer. Use sparkplugb only when you must interoperate with an existing SparkplugB ecosystem (Ignition, HiveMQ Edge, etc.).
4. Pair with a Reshape Before MQTT
Section titled “4. Pair with a Reshape Before MQTT”Most MQTT writers require the topic to be set explicitly. After the UNS connector, use a Reshape step that promotes _uns_topic to the publish topic field:
... → UNS → Reshape (move _uns_topic → topic) → MqttV5Writer5. Document Your UNS at the Org Level
Section titled “5. Document Your UNS at the Org Level”Pick an ISA-95 schema once for the whole organization and apply it consistently across all sites and lines. Schema drift across Meddle deployments creates incompatible topic trees.
Example Workflows
Section titled “Example Workflows”PLC → UNS → MQTT (Cloud Broker)
Section titled “PLC → UNS → MQTT (Cloud Broker)”ModbusReader → Reshape → UNS → Reshape → MqttV5Writer- ModbusReader: Pulls raw register data from a PLC
- Reshape: Renames raw addresses to human-readable keys
- UNS: Decorates with
_uns_topic - Reshape: Promotes
_uns_topicto the MQTTtopicfield - MqttV5Writer: Publishes to a cloud broker, organized under the UNS
SparkplugB Edge Gateway
Section titled “SparkplugB Edge Gateway”OpcuaReader → Predictive → UNS (sparkplugb) → MqttV5Writer- OpcuaReader: Aggregates from multiple OPC UA endpoints
- Predictive: Adds trend/RUL/health-score
- UNS: Wraps the enriched payload in a SparkplugB envelope
- MqttV5Writer: Publishes to a SparkplugB-capable broker (e.g., HiveMQ Edge → Ignition)
Multi-Site Aggregation
Section titled “Multi-Site Aggregation”[Site A pipeline] ─┐[Site B pipeline] ─┼─→ UNS → InfluxDb2Writer[Site C pipeline] ─┘Each site pipeline labels its data with its own UNS configuration, ensuring downstream consumers can disambiguate by topic alone.
Related Connectors
Section titled “Related Connectors”- MQTT v5 - Publish UNS-decorated payloads to a broker
- MQTT v3 - Legacy MQTT publishing
- Reshape - Promote
_uns_topicto the writer’s topic field - Filter - Filter by
_uns_topicpatterns - Router - Branch routing based on the UNS hierarchy
Additional Resources
Section titled “Additional Resources”- ISA-95 Standard - Enterprise-Control System Integration
- Unified Namespace Concept (Walker Reynolds) - Industry primer
- SparkplugB Specification (Eclipse Tahu) - Official spec PDF
- HiveMQ Sparkplug Resources - Reference broker and tooling
- Ignition Sparkplug Module - SCADA integration example