Skip to main content

Remote Plugin

warning

For technical reasons, the complete source code for these examples cannot be displayed here.
Please visit our Github repository for more examples and the complete source code.

The "optiMEAS Remote Plugin Module" is a highly flexible tool for connecting external applications to the smartCORE system, based on modern communication protocols and technologies. The underlying communication takes place via the UDP protocol (User Datagram Protocol), which is known for its low latency and efficiency, especially in real-time applications. The module uses the MsgPack format for efficient serialization and deserialization of data, enabling compact and fast data transfer. The interface for communication with the smartCORE system is via IPC (interprocess communication), whereby the configuration of producer and consumer channels can be customized. This enables the integration and control of external software in real time and supports the flexible expansion of existing systems. The documentation provides detailed examples to facilitate the development of custom plugins, allowing specific requirements to be implemented easily and efficiently. By utilizing these technologies, the module offers a robust and scalable solution for communication between smartCORE and external processes, ideal for applications in demanding industrial environments.

Notes on script development:

  • If you are working on Windows, make sure that scripts only contain \n, not \r\n

Tutorials

Development on external PC
Simple data write plugin
Data read and write plugin
[Advanced] Installation of new Python libraries (e.g., NumPy)

JSON configuration

Configuration of network parameters

KeywordExplanation
portUDP port on which the plugin listens (default: 61616)
localhostRestriction to "localhost only" communication (default: true)

Configuration of process control

NameExplanation
enableDetermines whether the process should be started and monitored (default: false)
logOutputTransfer output (stdout & stderr) of the process to the smartCORE log file (default: true)
watchdogTimeoutMaximum time between 2x IPC messages until the process is RESTARTED
disableKillAllProcessesKill all processes on startup or when problems occur (Default: false)
commandName (with optional path) of the process
argumentsCommand line arguments for the process

Configuration of producer channels ( producerChannels )

KeywordExplanation
nameName of the channel
dataTypeData type of the channel
physicalUnitUnit of the channel

Configuration of the consumer channel ( consumerChannels )

KeywordExplanation
nameName of the channel to be read

Example configuration

{
"module": "remote",
"factory": "remote",
"config": {
"port": 61616,
"localhost": true,
"process":
{
"enable": true,
"logOutput": false,
"watchdogTimeout": 60,
"disableKillAllProcesses": false,
"command": "i2c-sen5x-cpp",
"arguments": "--device-path=/dev/i2c-1 --interval=1"
},
"producerChannels": [
{
"name": "sen5x_pm1p0",
"dataType": "float",
"physicalUnit": "ug/m^3"
},
{
"name": "sen5x_pm2p5",
"dataType": "float",
"physicalUnit": "ug/m^3"
}
],
"consumerChannels": [
{
"name": "scd40_co2"
}
]
}
}

API/Protocol

The protocol consists of a largely static header specifying a command code and (if necessary) a JSON payload.

Header metadata

Offset and data typeNameDescription
[0] uint32_tmagicTokenProtocol recognition (fixed 0x45554C42)
[4] uint8_tversionProtocol version (currently always 1; open for extensions)
[5] uint8_tpayloadTypeSupport and differentiation of different payload types (currently always 2)
[6] uint16_treservedReserved for future extensions (header size must be divisible by 4)
[8] uint64_tsenderPidProcess ID of the sending process
[16] uint64_tsenderTime_msSETime in milliseconds when the packet was sent (for diagnostic purposes)
[24] uint16_tgroupIdentifier of the service to which the RPC call was sent or from which the response comes (fixed at 1000 here)
[26] uint16_tcommandNumber of the RPC call (see following table)

Commands

Command No.DescriptionExplanation
0LifeSignRequestsmartCORE status query
1LifeSignResponseResponse to LifeSignRequest
100WriteSamplesByNameSend individual samples with channel name
101ReadSamplesByNameRequestQuery individual channels via channel name
102ReadSamplesByNameResponseResponse to ReadSamplesByNameRequest (measured values)
200ChannelListRequestQuery channel list (assignment channel name => index)
201ChannelListResponseResponse to ChannelListRequest (channel list)
202WriteSamplesRequestSend samples (optionally with timestamp) via index
203WriteSamplesResponseOptional response to WriteSamplesRequest if a token was transferred for confirmation
204ReadSamplesBeginActivation of cyclic transmission of measured values by the smartCORE
205ReadSamplesContentMeasured values from cyclic transmission
206ReadSamplesEndDeactivation of cyclic transmission
300AlarmMessageRequestWriting an alarm to the smartCORE alarm center
301AlarmMessageResponseConfirmation of AlarmMessageRequest

Payload structure (JSON content)

byName Commands

Write values to smartCORE

RPC: WriteSamplesByName (client => smartCORE)

ParameterDescription
cArray of channels
nChannel name
vMeasured value
tTime stamp (optional)
{
"c": [
{
"n": "sen5x_pm1p0",
"v": 1.0099999904632568,
"t": 1720074467000000
},
{
"n": "sen5x_pm2p5",
"v": 2.009999990463257,
"t": 1720074467000000
}
]
}
Reading values from smartCORE (polling)

RPC: ReadSamplesByNameRequest (Client => smartCORE)

ParameterDescription
cArray of channel names
{
"c": [
"sen5x_pm1p0",
"sen5x_pm2p5"
]
}

RPC: ReadSamplesByNameResponse (smartCORE => Client)

ParameterDescription
cArray of channels
nChannel name
vMeasured value
tTime stamp
{
"c": [
{
"n": "sen5x_pm1p0",
"v": 1.0099999904632568,
"t": 1720074467000000
},
{
"n": "sen5x_pm2p5",
"v": 2.009999990463257,
"t": 1720074467000000
}
]
}

byIndex Commands

Query channel list

RPC: ChannelListRequest (Client => smartCORE)

An empty request can be sent here to query the names of all channels. Alternatively, only selected channel names can be queried:

ParameterDescription
cArray of channel names
fRequest for specific fields, e.g., "d" (data type) [optional]
{
"f": [
"d"
],
"c": [
"sen5x_pm1p0",
"sen5x_pm2p5"
]
}

RPC: ChannelListResponse (smartCORE => Client)

ParameterDescription
cArray of channels
nChannel name
iChannel index
wWritable (producer channel) [not available if false]
dData type (optional; if requested)
{
"c": [
{
"n": "sen5x_pm1p0",
"i": 0,
"w": true,
"d": "float"
},
{
"n": "sen5x_pm2p5",
"i": 1,
"d": "int32"
}
]
}
Write values to smartCORE

RPC: WriteSamplesRequest (client => smartCORE)

There are three possible payloads here:

  • Individual samples per channel
  • Multiple samples with time stamp per channel
  • Equidistant samples per channel
ParameterDescription
aToken to receive acknowledge packet (optional)
cArray of channels
iChannel index
vMeasured value
tTime stamp (optional)
sDifference in time for equidistant samples

Payload variant 1: individual samples per channel

{
"a": "xyz",
"t": 1720074467000000,
"c": [
{
"i": 0,
"v": 1.0099999904632568,
"t": 1720074467000000
},
{
"i": 1,
"v": 2.009999990463257,
}
]
}

Payload variant 2: Multiple samples with timestamp per channel

{
"a": "xyz",
"c": [
{
"i": 0,
"v": [
1,
2,
3
],
"t": [
1720074467000000,
1720074467000100,
1720074467000200
]
},
{
"i": 1,
"v": [
1,
2,
3
],
"t": 1720074467000000,
"s": 200
}
]
}

Payload variant 3: Equidistant samples per channel

{
"a": "xyz",
"t": 1720074467000000,
"s": 200,
"c": [
{
"i": 0,
"v": [
1,
2,
3
],
"t": 1720074467000000,
"s": 200
},
{
"i": 1,
"v": [
1,
2,
3
]
}
]
}

RPC: WriteSamplesResponse (smartCORE => Client)

If a token was specified in the request packet, the smartCORE sends a packet with the token for confirmation.

ParameterDescription
aToken from the request packet
{
"a": "xyz"
}
Read continuous values from smartCORE

RPC: ReadSamplesBegin (Client => smartCORE)

ParameterDescription
tTime in milliseconds between two packets (transmission interval)
nDesired number of samples (number of identical consumption intervals per transmission interval)
eEquidistant (without transmission of time stamp)
cList of channel indices
{
"t": 100,
"n": 10,
"e": true,
"c": [
2,
5
]
}

RPC: ReadSamplesContent (smartCORE => Client)

ParameterDescription
xConsecutive packet index since start (e.g., to determine data loss)
cArray of channels
iChannel index
vMeasured value
tTime stamp

Notes:

  • If fewer than the desired number of samples are available, only the available samples are transmitted.
  • If no current sample is available, only the "last value" is sent.

Payload variant 1: with time stamp (e = "false")

{
"x": 123,
"c": [
{
"i": 2,
"v": [
1.0099999904632568,
5.009999990463257,
6.009999990463257
],
"t": [
1720074467000000,
1720074467000100,
1720074467000200
]
},
{
"i": 5,
"v": [
1.0099999904632568,
5.009999990463257,
6.009999990463257
],
"t": [
1720074467000000,
1720074467000100,
1720074467000200
]
}
]
}

Payload variant 2: without timestamp (e = "true")

{
"x": 123,
"t": 1720074467000000,
"s": 100,
"c": [
{
"i": 2,
"v": [
1.0099999904632568,
5.0099999904632568,
6.0099999904632568
]
},
{
"i": 5,
"v": [
1.0099999904632568,
5.0099999904632568,
6.0099999904632568
]
}
]
}

RPC: ReadSamplesEnd (Client => smartCORE)

Empty request to stop transmission.