Skip to main content
MQTT Explorer includes native support for the Sparkplug B specification, an industrial IoT protocol that provides a standardized way to structure MQTT topics and payloads for SCADA systems.

What is Sparkplug B?

Sparkplug B is a specification that defines:
  • Topic namespace: Structured topic hierarchy for industrial devices
  • Payload encoding: Binary Protocol Buffer encoding for efficiency
  • State management: Birth/Death certificates for device lifecycle
  • Data types: Rich metric types including templates and datasets
Sparkplug B is widely used in industrial automation, manufacturing, oil & gas, and other SCADA applications.

Automatic Topic Detection

The Sparkplug decoder automatically activates when it detects topics matching the Sparkplug B pattern:
^spBv1\.0/[^/]+/[ND](DATA|CMD|DEATH|BIRTH)/[^/]+(/[^/]+)?$

Topic Structure

Sparkplug topics follow this format:
spBv1.0/{group_id}/{message_type}/{edge_node_id}/{device_id}
Components:
  • spBv1.0 - Namespace (Sparkplug B version 1.0)
  • {group_id} - Logical grouping of edge nodes
  • {message_type} - Message type (see below)
  • {edge_node_id} - Edge node identifier
  • {device_id} - Optional device identifier

Message Types

NBIRTH

Node Birth CertificatePublished when an edge node comes online. Contains node-level metrics and metadata.

DBIRTH

Device Birth CertificatePublished when a device connects. Contains all device metrics and their current values.

NDATA

Node DataPublished periodically with updated node metrics.

DDATA

Device DataPublished with updated device metrics. Most common message type.

NCMD

Node CommandCommands sent to control an edge node.

DCMD

Device CommandCommands sent to control a device.

NDEATH

Node Death CertificatePublished by the broker when a node disconnects (via Last Will).

DDEATH

Device Death CertificatePublished when a device disconnects.

Example Topics

spBv1.0/Sparkplug Devices/NBIRTH/JavaScript Edge Node

Decoder Implementation

The Sparkplug decoder uses the sparkplug-payload library to decode binary payloads:
import { get } from 'sparkplug-payload'
import { Base64Message } from '../../../backend/src/Model/Base64Message'
import { Decoder } from '../../../backend/src/Model/Decoder'
import { MessageDecoder } from './MessageDecoder'

const sparkplug = get('spBv1.0')

export const SparkplugDecoder: MessageDecoder = {
  formats: ['Sparkplug'],
  
  canDecodeTopic(topic: string) {
    return !!topic.match(
      /^spBv1\.0\/[^/]+\/[ND](DATA|CMD|DEATH|BIRTH)\/[^/]+(\/[^/]+)?$/u
    )
  },
  
  decode(input) {
    try {
      const message = Base64Message.fromString(
        JSON.stringify(
          sparkplug.decodePayload(new Uint8Array(input.toBuffer()))
        )
      )
      return { message, decoder: Decoder.SPARKPLUG }
    } catch {
      return {
        error: 'Failed to decode sparkplugb payload',
        decoder: Decoder.NONE,
      }
    }
  },
}

How It Works

1

Topic pattern matching

When a message arrives, the decoder checks if the topic matches the Sparkplug B pattern.
2

Binary payload decoding

The binary payload is decoded using the sparkplug-payload library’s Protocol Buffer decoder.
3

JSON conversion

The decoded payload is converted to JSON for display in the UI.
4

Visual indication

The UI displays “Decoded SparkplugB” label to indicate successful decoding.

Decoded Payload Structure

Sparkplug payloads contain a timestamp and array of metrics:
{
  "timestamp": 1638360000000,
  "metrics": [
    {
      "name": "my_boolean",
      "value": true,
      "type": "Boolean",
      "timestamp": 1638360000000
    },
    {
      "name": "my_double",
      "value": 3.14159,
      "type": "Double"
    },
    {
      "name": "Properties/hw_version",
      "value": "v1.2.3",
      "type": "String"
    }
  ]
}

Supported Metric Types

Sparkplug B supports a rich set of data types:

Primitive Types

TypeDescriptionExample
BooleanTrue/false valuetrue
Int88-bit signed integer127
Int1616-bit signed integer32000
Int3232-bit signed integer2147483647
Int6464-bit signed integer9223372036854775807
UInt88-bit unsigned integer255
UInt1616-bit unsigned integer65535
UInt3232-bit unsigned integer4294967295
UInt6464-bit unsigned integer18446744073709551615
Float32-bit floating point3.14
Double64-bit floating point3.14159265359
StringUTF-8 string"Hello"
DateTimeTimestamp (ms since epoch)1638360000000
TextLong-form text"Description..."

Complex Types

Represents tabular data with typed columns:
{
  "name": "my_dataset",
  "type": "DataSet",
  "value": {
    "numOfColumns": 2,
    "types": ["String", "String"],
    "columns": ["str1", "str2"],
    "rows": [
      ["x", "a"],
      ["y", "b"]
    ]
  }
}
Reusable metric structure definitions:
{
  "name": "Template1",
  "type": "Template",
  "value": {
    "isDefinition": true,
    "metrics": [
      { "name": "myBool", "value": false, "type": "Boolean" },
      { "name": "myInt", "value": 0, "type": "UInt32" }
    ],
    "parameters": [
      {
        "name": "param1",
        "type": "String",
        "value": "value1"
      }
    ]
  }
}
Template instances reference the definition:
{
  "name": "TemplateInstance1",
  "type": "Template",
  "value": {
    "templateRef": "Template1",
    "isDefinition": false,
    "metrics": [
      { "name": "myBool", "value": true, "type": "Boolean" },
      { "name": "myInt", "value": 100, "type": "Int8" }
    ]
  }
}

Birth Certificates

Birth certificates establish the initial state and metadata for nodes and devices.

Node Birth Example

{
  "timestamp": 1638360000000,
  "metrics": [
    {
      "name": "Node Control/Rebirth",
      "type": "Boolean",
      "value": false
    },
    {
      "name": "Template1",
      "type": "Template",
      "value": {
        "isDefinition": true,
        "metrics": [
          { "name": "myBool", "value": false, "type": "Boolean" },
          { "name": "myInt", "value": 0, "type": "UInt32" }
        ]
      }
    }
  ]
}

Device Birth Example

{
  "timestamp": 1638360000000,
  "metrics": [
    { "name": "Inputs/0", "value": true, "type": "Boolean" },
    { "name": "Inputs/1", "value": 0, "type": "Int8" },
    { "name": "Outputs/0", "value": false, "type": "Boolean" },
    { "name": "Properties/hw_version", "value": "v1.0.0", "type": "String" },
    { "name": "Properties/sw_version", "value": "v2.1.3", "type": "String" }
  ]
}
Birth certificates should include all metrics a device supports. Data messages only need to include changed metrics.

Testing Sparkplug Decoding

MQTT Explorer includes a mock Sparkplug client for testing:
1

Start the mock client

The test suite includes a Sparkplug simulator that publishes realistic NBIRTH, DBIRTH, and DDATA messages.
2

Connect to your broker

Ensure your broker is running and MQTT Explorer is connected.
3

Navigate to Sparkplug topics

Expand the tree to find topics like:
spBv1.0/Sparkplug Devices/DDATA/JavaScript Edge Node/Emulated Device
4

Verify automatic decoding

The decoder should automatically activate and display “Decoded SparkplugB” with formatted JSON output.

Mock Client Configuration

The mock client is configured in src/spec/mock-sparkplugb.ts:
const config = {
  serverUrl: `tcp://${brokerHost}:${brokerPort}`,
  groupId: 'Sparkplug Devices',
  edgeNode: 'JavaScript Edge Node',
  clientId: 'JavaScriptSimpleEdgeNode',
  version: 'spBv1.0',
}

const deviceId = 'Emulated Device'

State Management

Sparkplug B includes sophisticated state management:

Node Control/Rebirth

The Node Control/Rebirth metric allows SCADA systems to request a rebirth:
sparkplugClient.on('ncmd', (payload: UPayload) => {
  const { metrics } = payload
  
  for (let i = 0; i < metrics.length; i++) {
    const metric = metrics[i]
    if (metric.name == 'Node Control/Rebirth' && metric.value) {
      // Republish NBIRTH and DBIRTH certificates
      sparkplugClient.publishNodeBirth(getNodeBirthPayload())
      sparkplugClient.publishDeviceBirth(deviceId, getDeviceBirthPayload())
    }
  }
})

Device Commands

Devices respond to DCMD messages:
sparkplugClient.on('dcmd', (deviceId: string, payload: UPayload) => {
  const { metrics } = payload
  const updates = []
  
  for (const metric of metrics) {
    if (metric.name === 'Outputs/0') {
      // Update output and echo back
      updates.push({ 
        name: 'Outputs/0', 
        value: metric.value, 
        type: 'Boolean' 
      })
    }
  }
  
  // Publish DDATA with updates
  sparkplugClient.publishDeviceData(deviceId, {
    timestamp: new Date().getTime(),
    metrics: updates
  })
})

Best Practices

Use Birth Certificates

Always publish complete BIRTH messages with all metrics when connecting

Set Last Will

Configure NDEATH as the Last Will and Testament (LWT) message

Include Timestamps

Add timestamps to metrics for time-series data integrity

Namespace Your Metrics

Use hierarchical names like Properties/hw_version for organization

Minimize DDATA Size

Only send changed metrics in data messages

Use Templates

Define templates for repeated metric structures
Binary Protocol BuffersSparkplug payloads are binary-encoded using Protocol Buffers. Do not attempt to parse them as JSON or text - always use the Sparkplug decoder.

Dependencies

Sparkplug B support requires these packages:
{
  "dependencies": {
    "sparkplug-payload": "1.0.3"
  },
  "devDependencies": {
    "sparkplug-client": "3.2.4"
  }
}
  • sparkplug-payload: Encoder/decoder for Sparkplug B payloads
  • sparkplug-client: Full Sparkplug B client (used in tests)

Troubleshooting

Verify the topic matches the Sparkplug pattern exactly:
spBv1.0/{group_id}/[ND](DATA|CMD|DEATH|BIRTH)/{edge_node_id}/{device_id}
Common issues:
  • Wrong version (must be spBv1.0)
  • Invalid message type (must be DATA, CMD, DEATH, or BIRTH)
  • Missing edge node ID
This error indicates the binary payload is not valid Sparkplug B:
  • Check that the publisher is using sparkplug-payload or compatible encoder
  • Verify the payload is binary (not JSON text)
  • Try publishing from the mock client to confirm the decoder works
This is normal behavior. Sparkplug B only requires changed metrics in data messages:
  • BIRTH messages contain all metrics
  • DATA messages only contain updates
  • Use the birth certificate as the schema reference

See Also

Build docs developers (and LLMs) love