Remote Plugin
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
| Keyword | Explanation |
|---|---|
| port | UDP port on which the plugin listens (default: 61616) |
| localhost | Restriction to "localhost only" communication (default: true) |
Configuration of process control
| Name | Explanation |
|---|---|
| enable | Determines whether the process should be started and monitored (default: false) |
| logOutput | Transfer output (stdout & stderr) of the process to the smartCORE log file (default: true) |
| watchdogTimeout | Maximum time between 2x IPC messages until the process is RESTARTED |
| disableKillAllProcesses | Kill all processes on startup or when problems occur (Default: false) |
| command | Name (with optional path) of the process |
| arguments | Command line arguments for the process |
Configuration of producer channels ( producerChannels )
| Keyword | Explanation |
|---|---|
| name | Name of the channel |
| dataType | Data type of the channel |
| physicalUnit | Unit of the channel |
Configuration of the consumer channel ( consumerChannels )
| Keyword | Explanation |
|---|---|
| name | Name 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
Header metadata
| Offset and data type | Name | Description |
|---|---|---|
| [0] uint32_t | magicToken | Protocol recognition (fixed 0x45554C42) |
| [4] uint8_t | version | Protocol version (currently always 1; open for extensions) |
| [5] uint8_t | payloadType | Support and differentiation of different payload types (currently always 2) |
| [6] uint16_t | reserved | Reserved for future extensions (header size must be divisible by 4) |
| [8] uint64_t | senderPid | Process ID of the sending process |
| [16] uint64_t | senderTime_msSE | Time in milliseconds when the packet was sent (for diagnostic purposes) |
| [24] uint16_t | group | Identifier of the service to which the RPC call was sent or from which the response comes (fixed at 1000 here) |
| [26] uint16_t | command | Number of the RPC call (see following table) |
Commands
| Command No. | Description | Explanation |
|---|---|---|
| 0 | LifeSignRequest | smartCORE status query |
| 1 | LifeSignResponse | Response to LifeSignRequest |
| 100 | WriteSamplesByName | Send individual samples with channel name |
| 101 | ReadSamplesByNameRequest | Query individual channels via channel name |
| 102 | ReadSamplesByNameResponse | Response to ReadSamplesByNameRequest (measured values) |
| 200 | ChannelListRequest | Query channel list (assignment channel name => index) |
| 201 | ChannelListResponse | Response to ChannelListRequest (channel list) |
| 202 | WriteSamplesRequest | Send samples (optionally with timestamp) via index |
| 203 | WriteSamplesResponse | Optional response to WriteSamplesRequest if a token was transferred for confirmation |
| 204 | ReadSamplesBegin | Activation of cyclic transmission of measured values by the smartCORE |
| 205 | ReadSamplesContent | Measured values from cyclic transmission |
| 206 | ReadSamplesEnd | Deactivation of cyclic transmission |
| 300 | AlarmMessageRequest | Writing an alarm to the smartCORE alarm center |
| 301 | AlarmMessageResponse | Confirmation of AlarmMessageRequest |
Payload structure (JSON content)
byName Commands
Write values to smartCORE
RPC: WriteSamplesByName (client => smartCORE)
| Parameter | Description |
|---|---|
| c | Array of channels |
| n | Channel name |
| v | Measured value |
| t | Time 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)
| Parameter | Description |
|---|---|
| c | Array of channel names |
{
"c": [
"sen5x_pm1p0",
"sen5x_pm2p5"
]
}
RPC: ReadSamplesByNameResponse (smartCORE => Client)
| Parameter | Description |
|---|---|
| c | Array of channels |
| n | Channel name |
| v | Measured value |
| t | Time 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:
| Parameter | Description |
|---|---|
| c | Array of channel names |
| f | Request for specific fields, e.g., "d" (data type) [optional] |
{
"f": [
"d"
],
"c": [
"sen5x_pm1p0",
"sen5x_pm2p5"
]
}
RPC: ChannelListResponse (smartCORE => Client)
| Parameter | Description |
|---|---|
| c | Array of channels |
| n | Channel name |
| i | Channel index |
| w | Writable (producer channel) [not available if false] |
| d | Data 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
| Parameter | Description |
|---|---|
| a | Token to receive acknowledge packet (optional) |
| c | Array of channels |
| i | Channel index |
| v | Measured value |
| t | Time stamp (optional) |
| s | Difference 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.
| Parameter | Description |
|---|---|
| a | Token from the request packet |
{
"a": "xyz"
}
Read continuous values from smartCORE
RPC: ReadSamplesBegin (Client => smartCORE)
| Parameter | Description |
|---|---|
| t | Time in milliseconds between two packets (transmission interval) |
| n | Desired number of samples (number of identical consumption intervals per transmission interval) |
| e | Equidistant (without transmission of time stamp) |
| c | List of channel indices |
{
"t": 100,
"n": 10,
"e": true,
"c": [
2,
5
]
}
RPC: ReadSamplesContent (smartCORE => Client)
| Parameter | Description |
|---|---|
| x | Consecutive packet index since start (e.g., to determine data loss) |
| c | Array of channels |
| i | Channel index |
| v | Measured value |
| t | Time 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.