Skip to content

Sparkplug

Overview

The Sparkplug is a microservice that acts as Sparkplug B Edge of Network (EoN) node to communicate with other Sparkplug-enabled applications, such as SCADA/IIOT host.

The implementation of Sparkplug follows Sparkplug Specification Version 2.2 to support following messages and their corresponding flows:

  • EoN Death Certificate (NDEATH)
  • EoN Birth Certificate (NBIRTH)
  • Device Birth Certificate (DBIRTH)
  • Device Death Certificate (DDEATH)
  • Device Data Messages (DDATA)
  • Device Command (DCMD)

Note

The current architecture of Edge Xpert do not have any MQTT EoN node data, so that the Sparkplug neither publish NDATA messages nor process NCMD messages.

Default Configuration

By default, the Sparkplug uses the following configuration:

[Writable]
LogLevel = "INFO"
  [Writable.InsecureSecrets]
    [Writable.InsecureSecrets.DB]
    path = "redisdb"
      [Writable.InsecureSecrets.DB.Secrets]
      username = ""
      password = ""
    [Writable.InsecureSecrets.Sparkplug]
    path = "sparkplug"
      [Writable.InsecureSecrets.Sparkplug.Secrets]
      cacert = ""
[Service]
HealthCheckInterval = "10s"
Host = "localhost"
Port = 59996
ServerBindAddr = "" # Leave blank so default to Host value unless different value is needed.
StartupMsg = "This is the Sparkplug Microservice"
MaxResultCount = 1024
MaxRequestSize = 0 # Not curently used. Defines the maximum size of http request body in bytes
RequestTimeout = "5s"
  [Service.CORSConfiguration]
  EnableCORS = false
  CORSAllowCredentials = false
  CORSAllowedOrigin = "https://localhost"
  CORSAllowedMethods = "GET, POST, PUT, PATCH, DELETE"
  CORSAllowedHeaders = "Authorization, Accept, Accept-Language, Content-Language, Content-Type, X-Correlation-ID"
  CORSExposeHeaders = "Cache-Control, Content-Language, Content-Length, Content-Type, Expires, Last-Modified, Pragma, X-Correlation-ID"
  CORSMaxAge = 3600

[Registry]
Host = "localhost"
Port = 8500
Type = "consul"

[Clients]
  [Clients.core-metadata]
  Protocol = "http"
  Host = "localhost"
  Port = 59881

  [Clients.core-command]
  Protocol = "http"
  Host = "localhost"
  Port = 59882

  [Clients.support-notifications]
  Protocol = "http"
  Host = "localhost"
  Port = 59860

[Databases]
  [Databases.Primary]
  Host = "localhost"
  Name = "coredata"
  Port = 6379
  Timeout = 5000
  Type = "redisdb"

[MessageQueue]
Protocol = "mqtt"
Host = "localhost"
Port = 1883
Type = "mqtt"
AuthMode = "none"
SecretName = "mqtt-bus"
SubscribeEnabled = true
SubscribeTopic = "edgex/events/device/#"  # required for subscribing to Events from MessageBus
  [MessageQueue.Optional]
  # Default MQTT Specific options that need to be here to enable evnironment variable overrides of them
  # Client Identifiers
  ClientId ="support-sparkplug"
  # Connection information
  Qos          =  "0" # Quality of Sevice values are 0 (At most once), 1 (At least once) or 2 (Exactly once)
  KeepAlive    =  "10" # Seconds (must be 2 or greater)
  Retained     = "false"
  AutoReconnect  = "true"
  ConnectTimeout = "5" # Seconds
  # TLS configuration - Only used if Cert/Key file or Cert/Key PEMblock are specified
  SkipCertVerify = "false"

[SecretStore]
Type = "vault"
Protocol = "http"
Host = "localhost"
Port = 8200
Path = "support-sparkplug/"
TokenFile = "/tmp/edgex/secrets/support-sparkplug/secrets-token.json"
RootCaCertPath = ""
ServerName = ""
  [SecretStore.Authentication]
  AuthType = "X-Vault-Token"
  [SecretStore.RuntimeTokenProvider]
  Enabled = false
  Protocol = "https"
  Host = "localhost"
  Port = 59841
  TrustDomain = "edgexfoundry.org"
  EndpointSocket = "/tmp/edgex/secrets/spiffe/public/api.sock"
  RequiredSecrets = "redisdb"

[Sparkplug]
Namespace = "spBv1.0"
GroupId = "group"
EdgeNodeId = "node"
  [Sparkplug.MqttBroker]
  Url = "tcp://mqtt-broker:1883"
  ClientIdPrefix = "edgex-sparkplug"
  ConnectTimeout = "30s"
  AutoReconnect = false
  KeepAlive = "60s"
  QoS = 0
  Retain = false
  SkipCertVerify = false
  SecretPath = "sparkplug"
  AuthMode = "none"
  RetryDuration = 600
  RetryInterval = 5
  [Sparkplug.Payload]
  MetricNameFormat = "{metric_level1}/{metric_level2}/{resourceName}"
  [Sparkplug.DeviceChangesNotifications]
  MqttSource = "edgex-messagequeue"
  SubscribeTopic = "iotech/notification"
  ContentPrefix = "Metadata notice: "
  Label = "metadata"

Similar to other EdgeX microservices, the Sparkplug uses the same common configuration. Sparkplug is the only unique configuration used by the Sparkplug.

The Sparkplug configuration defines Sparkplug-related information:

Property Default Description
Namespace spBv1.0 Specifies both the root element of Sparkplug topic structure and the encoding used for the associated payload data. As the Sparkplug supports Sparkplug B specification only, it is recommended to keep the default value spBv1.0.
GroupId group Specifies the logical grouping of MQTT EoN nodes. This configuration determines the group_id element of the Sparkplug topic structure. Per Sparkplug B specification, the value of this configuration can be any valid UTF-8 alphanumeric string except for the reserved characters of ‘+’ (plus), ‘/’ (forward slash), and ‘#’ (number sign). In most use cases to minimize bandwidth, it should be descriptive but as small as possible.
EdgeNodeId node Specifies the edge_node_id element of the Sparkplug topic structure.
Property Default Description
Url tcp://mqtt-broker:1883 Specifies the Url to access to the Sparkplug MQTT broker.
ClientIdPrefix edgex-sparkplug Specifies the prefix of client Id to connect with Sprakplug MQTT broker. While connecting to Sparkplug MQTT Broker, the Sparkplug will append a random string to this prefix as the full client Id.
ConnectTimeout 30s Specifies how long the Sparkplug will wait when trying to open a connection to the Sparkplug MQTT broker before timing out. A duration of 0 never times out. Default 30 seconds.
AutoReconnect false Specifies whether the automatic reconnection logic should be used when the connection to the Sparkplug MQTT broker is lost.
KeepAlive 60s Specifies the amount of time (in seconds) that the Sparkplug should wait before sending a PING request to the Sparkplug MQTT broker. This will allow the service to know that a connection has not been lost with the broker.
QoS 0 Specifies the Quality of Service (QoS) levels for the connection with the Sparkplug MQTT broker.
Retain false Specifies the retained message setting for the MQTT connection.
SkipCertVerify false Specifies whether the Sparkplug verifies the Sparkplug MQTT broker's certificate chain and host name.
AuthMode none Specifies the authentication mode to access to the Sparkplug MQTT broker. Valid values are none, usernamepassword, cacert, and clientcert. See note below for more explanation.
SecretPath sparkplug Specifies the path in the secret provider from which to retrieve secrets of the Sparkplug MQTT broker.

When the Sparkplug is running under the secure mode, secrets will be retrieved from Secret Store; when the Sparkplug is running under insecure mode, secrets will be retrieved from Writable.InsecureSecrets configuration.
RetryDuration 600 Specifies how long (in seconds) to wait timing out for the Sparkplug to establish the connection with the Sparkplug MQTT broker.
RetryInterval 5 Specifies the time (in seconds) to be waited between each attempts for the Sparkplug to establish the connection with the Sparkplug MQTT broker.
Property Default Description
MetricNameFormat {metric_level1}/{metric_level2}/{resourceName} Specifies the name of Sparkplug metric. Per Sparkplug specification, the metric name may be hierarchical to build out proper folder structures for applications consuming the metric values. Note that any curly brackets variable, e.g. {key-name}, will be regarded as placeholder text and will be replaced by corresponding device resource tags during service runtime.
Property Default Description
MqttSource edgex-messagequeue Specifies which mqtt broker configuration should be used for publishing DeviceCreationNotification. Set MqttSource to edgex-messagequeue when DeviceCreationNotification should be published to the EdgeX message queue. Set MqttSource to sparkplug-mqtt when DeviceCreationNotification should be published to the Sparkplug MQTT broker.
SubscribeTopic iotech/notification Specifies the topic to subscribe for DeviceChangesNotifications.
ContentPrefix Metadata notice: Specifies the content prefix of DeviceChangesNotifications. The value must match to the Notifications.Content configuration of core-metadata service.
Label metadata Specifies the label of device change messages. The value must match to the Notifications.Label configuration of core-metadata service.

Note

AuthMode accepts usernamepassword, clientcert, cacert, or none. When AuthMode is usernamepassword, following secret keys are mandatory and must exist in the specified SecretPath of secret provider:

  • username - specifies the username to authenticate with MQTT broker.
  • password - specifies the password to authenticate with MQTT broker.
When AuthMode is clientcert, the following secret keys are mandatory and must exist in the specified SecretPath of secret provider:
  • clientkey - specifies the client private key in PEM format.
  • clientcert - specifies the client certificate in PEM format.
When AuthMode is cacert, the following secret key is mandatory and must exist in the specified SecretPath of secret provider:
  • cacert - specifies the CA certificate in PEM format.
When AuthMode is none, no secret is required.

Note

The Sparkplug relies on the Device Changes Notification feature to detect if any device is created or removed from Core Metadata. To ensure that Sprakplug can properly handle device-level messages (DBIRTH, DDEATH, and DDATA), Device Changes Notification feature must be enabled on Core Metadata.

Override Default Configuration

There are two ways to override the default configuration of the Sparkplug.

Override Specific Configuration with Environment Variables

To override specific configuration settings, you can specify environment variables directly inside compose files. In the following example, a couple of Sparkplug configurations are overridden by environment variables:

  support-sparkplug:
    image: iotechsys/${EDGEXPERT_IMAGE_REPO}-support-sparkplug:${EDGEXPERT_IMAGE_VERSION}
    container_name: support-sparkplug
    read_only: true
    security_opt:
      - no-new-privileges:true
    networks:
      edgex-network:
        aliases:
          - edgex-support-sparkplug
    user: 2002:2001
    environment:
      <<: *common-variables
      SERVICE_HOST: support-sparkplug
      SPARKPLUG_NAMESPACE: spBv1.0
      SPARKPLUG_GROUPID: IOTech
      SPARKPLUG_EDGENODEID: MU_001
      SPARKPLUG_PAYLOAD_METRICNAMEFORMAT: {Sunspec_Model}/{Sub_Device}/{Tag_Name_n}/{resourceName}
      SPARKPLUG_MQTTBROKER_URL: tcps://172.17.0.1:8883
      SPARKPLUG_MQTTBROKER_AUTHMODE: cacert
      SPARKPLUG_MQTTBROKER_SECRETPATH: ignition
    logging: *default-logging
    volumes:
      # use host timezone
      - /etc/localtime:/etc/localtime:ro
      - license-data:/edgexpert/licenses/:ro
    depends_on:
      - core-metadata
      - support-notifications

Override with a .env file

To override these configurations, you can also prepare /etc/edgexpert/.env with replaced values inside. A sample is shown as below:

SPARKPLUG_NAMESPACE=spBv1.0
SPARKPLUG_GROUPID=IOTech
SPARKPLUG_EDGENODEID=Node_001
SPARKPLUG_PAYLOAD_METRICNAMEFORMAT={Sunspec_Model}/{Sub_Device}/{Tag_Name_n}/{resourceName}
SPARKPLUG_MQTTBROKER_URL=tcps://172.17.0.1:8883
SPARKPLUG_MQTTBROKER_AUTHMODE=cacert
SPARKPLUG_MQTTBROKER_SECRETPATH=ignition
NOTIFICATIONS_POSTDEVICECHANGES=true

Run the Sparkplug through Edge Xpert CLI

The below example assumes that you have installed and launched Ignition as the Sparkplug SCADA system with TLS communication enabled:

Once you had installed and launched Ignition MQTT modules, you should have obtained Sparkplug MQTT broker connection information.

  1. Prepare the /etc/edgexpert/.env with your Sparkplug MQTT broker configuration. A sample is shown as below:
    SPARKPLUG_NAMESPACE=spBv1.0
    SPARKPLUG_GROUPID=IOTech
    SPARKPLUG_EDGENODEID=Node_001
    SPARKPLUG_PAYLOAD_METRICNAMEFORMAT={Sunspec_Model}/{Sub_Device}/{Tag_Name_n}/{resourceName}
    SPARKPLUG_MQTTBROKER_URL=tcps://172.17.0.1:8883
    SPARKPLUG_MQTTBROKER_AUTHMODE=cacert
    SPARKPLUG_MQTTBROKER_SECRETPATH=ignition
    NOTIFICATIONS_POSTDEVICECHANGES=true
    
  2. It is recommended to run the Sparkplug through the edgexpert up utility. Run the following command to launch the Sparkplug under the secure mode:
    edgexpert up --secret xpert-manager support-notifications support-sparkplug 
    
  3. If you had enabled TLS for Ignition MQTT communication, you will have to post the certificate cacert into secret store, so that the Sparkplug can properly access to the MQTT broker. It is recommended to use POST secrets REST API of Sparkplug service to add secrets into secret store.

    Info

    The detailed steps to add secrets into secret store are listed here.

  4. After posting the cacert secret into secret store, the Sparkplig should be able to establish connection with Ignition Sparkplug MQTT broker, and publish an NBIRTH message via topic spBv1.0/IOTech/NBIRTH/Node_001 to the MQTT broker. You can launch an Ignition Designer to observe such behaviour. Under Tag Browser>MQTT Engine, you can see that an Edge of Network node is created:
    IgnitionDesigner1

  5. Launch the Virtual Device Service to simulate new devices registered with the Edge of Network node (DBIRTH):

    edgexpert up --secret device-virtual
    

  6. Switch to the Ignition Designer, you will observe that new devices under the Virtual Device Service have been added (DBIRTH), and values will be updated periodically (DDATA):
    devicesAdded
  7. Ignition Designer can also issue device command (DCMD) messages to the Sparkplug. To simulate the DCMD through Ignition Designer, ensure that you disable Block Device Commands setting for MQTT Engine as shown below:
    CommandSetting
    Update any tag value, e.g. Int8, under a device, e.g. Random-Integer-Device, as shown below:
    DCMD
    After the Sparkplug successfully processes the DCMD message, you can observe a similar log coming from the Sparkplug as shown below:
    level=INFO ts=2022-07-26T20:56:03.764207175+08:00 app=support-sparkplug source=dcmd.go:134 msg="successfully issue SET command (deviceName:Random-Integer-Device, commandName:Int8, setting:map[Int8:-2]) via Command Client"
    
  8. To simulate the device removal (DDEATH), you can launch Edge Xpert Manager GUI via browser at http://localhost:9090/, switch to Devices and then delete some devices, e.g. Random-Integer-Device, to trigger the removal of devices, which will cause the Sparkplug to publish DDEATH for corresponding devices:
    deviceDeleted
  9. Switch to the Ignition Designer, you will observe that deleted devices under the Virtual Device Service have became STALE (DDEATH), and values are no longer updated periodically:
    DDEATH
  10. To simulate the Edge of Network node death (NDEATH), you have to force kill the Sparkplug. A graceful shutdown will not trigger NDEATH to be published by MQTT Broker:
    docker kill support-sparkplug
    
  11. Switch to the Ignition Designer, you will observe that the Death Count of Edge of Network node is incremented:
    NDEATH

Post the secrets into the secret store via the REST API of Sparkplug

Follow the steps below to use POST secrets REST API of Sparkplug to add secrets into secret store:

  1. Inspect the IPAddress of the running Sparkplug:

    docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' support-sparkplug
    172.17.0.6
    

  2. Users may find that it is difficult to properly add the content of X.509 PEM files in valid format in a REST request. Use the following awk command to convert the content of X.509 PEM files as a string:

    awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' cert.pem
    -----BEGIN CERTIFICATE-----\nMIIDgzCCAmugAwIBAgIUXvDwsS+pW/W+hMRp675EcXsYwIgwDQYJKoZIhvcNAQEF\n-----END CERTIFICATE-----\n
    

Note

Take a note on the console output of the awk command result.

  1. Insert the copied string into the payload of REST call to add the secret. Note that all necessary secrets must be added in a single REST call, otherwise the secrets content will be overwritten by REST calls afterwards. For example, the following curl command adds one secret--cacert--under the secret path ignition in the Vault secret store:
    curl -k -X POST \
    http://172.17.0.6:59996/api/v2/secret \ 
    -d '{
       "apiVersion":"v2",
       "path":"ignition",
       "secretData":[
          {
             "key":"cacert",
             "value":"-----BEGIN CERTIFICATE-----\nMIIDgzCCAmugAwIBAgIUXvDwsS+pW/W+hMRp675EcXsYwIgwDQYJKoZIhvcNAQEF\n-----END CERTIFICATE-----\n"
          }
       ]  
    }'