Sparkplug Templates: Put Shared Device Definitions in NBIRTH Before Publishing Instances
Two pumps can share one Sparkplug custom type without sharing every instance parameter. The publisher must put the common declaration in the edge node’s NBIRTH, then reference it from each device’s DBIRTH. Copying the declaration into each DBIRTH does not satisfy the Sparkplug 3.0.0 template contract.
The important separation is between member metrics, which describe the type’s reported values, and parameters, which provide template metadata. A parameter default can supply an omitted parameter value. It does not fill a missing member metric in a birth instance.
Define the type at the edge-node boundary
Eclipse Sparkplug 3.0.0, sections 6.4.14–6.4.15, requires template definitions in NBIRTH. A definition sets is_definition to true, omits template_ref, and declares its members and parameters. An instance sets the flag to false and refers to the definition metric’s name.
For a synthetic gateway, choose the definition name PumpType. Declare two member metrics, running with Boolean datatype and pressure with Float datatype. Add a String parameter named area, with the default utility. These names and values are invented for this design exercise; they are not observations from a plant.
The definition name is a reference target. Keep it stable and match it exactly in the fixtures. This example does not introduce a mandatory naming prefix or assume that a host will turn slashes in names into a particular folder layout.

Instantiate both devices completely at birth
Device A’s DBIRTH contains a template instance referencing PumpType, with running = false and pressure = 0.0. It omits area, so the definition’s default applies. Device B references the same type, includes both member metrics, and explicitly supplies area = process.
A compact diagnostic outline is:
NBIRTH: PumpType
is_definition: true
metrics: running(Boolean), pressure(Float)
parameters: area(String, default="utility")
template_ref: omitted
DBIRTH device A: Pump
is_definition: false; template_ref: "PumpType"
metrics: running=false, pressure=0.0
parameters: omitted -> area="utility"
DBIRTH device B: Pump
is_definition: false; template_ref: "PumpType"
metrics: running=false, pressure=0.0
parameters: area="process"
This is a readable explanation of selected fields, not a complete payload or alternate wire format. A real message still needs the applicable Sparkplug message-level fields and binary Sparkplug B serialization. A JSON inspection tool, if used during development, should be understood as displaying the decoded binary structure.
The two birth instances each contain every declared member. After birth, a DDATA template instance can report a subset of members. Keep those constructors distinct so a convenient change-only data builder cannot accidentally create an incomplete DBIRTH.
Review defaults independently from member completeness
Implement two separate checks in a proposed publisher validator. The first compares birth-instance member names against the declared set. The second resolves declared parameter values and any available defaults. Combining these into one generic “missing fields use defaults” function creates the exact defect this example is intended to expose.
For Device A, resolving area should produce utility; it should not create a missing pressure measurement. For Device B, the explicit parameter should win over that default. A parameter with no supplied value and no default needs a deliberate application treatment rather than an invented process value.
Keep a resolved diagnostic view beside the encoded fixture. That makes an unexpected host display easier to investigate: the publisher can show the reference, complete member set and effective metadata before asking whether a particular host presents templates as expected.
Reject malformed fixtures before commissioning
Prepare four negative cases: move the sole definition into DBIRTH; use an unknown template_ref; add an undeclared member; and remove pressure from one birth instance. Each should fail the relevant static check. Also prepare a later DDATA instance containing only pressure so the validator does not wrongly apply birth completeness to every update.
These are proposed protocol checks, not executed serializer or host interoperability tests. After implementing them, qualify the actual serializer and intended host with the valid two-device example. The acceptance evidence should preserve the NBIRTH declaration and both decoded DBIRTH instances together. A screenshot of two values alone cannot show that the shared type was declared at the correct boundary.