DNP3
Pull data from DNP3 outstations into AnyLog as master over TCP or TLS using run plc client.
AnyLog can act as a DNP3 master over TCP or TLS (using hostname and port, default 20000).
Data is read on a schedule and streamed into your local operator database as JSON, using the same run plc client
pattern as Modbus, OPC-UA, and EtherNet/IP.
For a production-style deployment of a DNP3 connection from a standing script (rather than one-off CLI commands), see Deploying a DNP3 Connector via Script. For reusing a single point map across multiple outstations instead of repeating it inline, see DNP3 Mapping Policies.
Prerequisites
| Requirement | Notes |
|---|---|
opendnp3 (yadnp3) |
Must be installed in the AnyLog runtime environment. |
Declare the target DBMS before streaming, for example:
connect dbms new_company where type = sqlite
Connection and map
DNP3 uses hostname and port. master_id and outstation_id are the DNP3 link addresses (defaults: master 1, outstation 10).
map is a JSON array of points. Each object must include:
| Key | Required | Meaning |
|---|---|---|
name |
yes | Column / logical label |
index |
yes | Point index on the outstation |
type |
one of type or group+variation |
Point class (see table below) |
group, variation |
alternative to type |
Raw DNP3 group/variation |
Supported type values (case-insensitive; spaces/underscores ignored):
type |
DNP3 group / variation |
|---|---|
Analog |
30 / 6 |
Binary |
1 / 2 |
BinaryOutputStatus |
10 / 2 |
AnalogOutputStatus or AnalogOutput |
40 / 4 |
Counter |
20 / 6 |
DoubleBit or DoubleBitBinary |
3 / 2 |
map can be written inline, as shown throughout this page, or declared once as a reusable policy on the
blockchain and referenced across multiple connections — see DNP3 Mapping Policies.
Optional TLS (all three PEM paths required when enable_tls = true):
| Keyword | Meaning |
|---|---|
enable_tls |
Use TLS instead of plain TCP |
tls_ca |
CA certificate (PEM file path) |
tls_cert |
Client certificate (PEM file path) |
tls_key |
Client private key (PEM file path) |
One-shot read
<get plc values where type = dnp3 and
hostname = 127.0.0.1 and
port = 20001 and
master_id = 1 and
outstation_id = 10 and
map = [{"name":"analog_0","type":"Analog","index":0},
{"name":"binary_0","type":"Binary","index":0},
{"name":"counter_0","type":"Counter","index":0},
{"name":"doublebit_0","type":"DoubleBit","index":0}]
>
Alias: get dnp3 values (same keywords).
With TLS (see DNP3 TLS Test Certificates for generating a chain to use here, and DNP3 Out Station Testing below for running a matching test outstation):
<get dnp3 values where
hostname = 127.0.0.1 and
port = 20001 and
master_id = 1 and
outstation_id = 10 and
enable_tls = true and
tls_ca = /path/to/your/working/dir/anylogDNP3ca.cert and
tls_cert = /path/to/your/working/dir/master1.cert and
tls_key = /path/to/your/working/dir/master1.key and
map = [{"name":"analog_0","type":"Analog","index":0}]
>
Continuous ingest — wide table (default)
With table = ... and dbms, all points from map land in one table. Each poll inserts one row; every object in map is one column: the map name is the column name, and that column stores the value read for that point.
<run plc client where type = dnp3 and
hostname = 127.0.0.1 and
port = 20001 and
master_id = 1 and
outstation_id = 10 and
frequency = 20 and
name = dnp3_rtu and
dbms = new_company and
table = substation and
map = [{"name":"analog_0","type":"Analog","index":0}]
>
Continuous ingest — dynamic tables (dynamic = true)
Omit table and omit namespace for plain dynamic ingest. Each object in map is written to its own table. The table name is derived from the client name and the map name (for example, dnp3_rtu_analog_0 when name = dnp3_rtu and the map entry’s name is analog_0). Each row includes timestamp, tag, and value.
<run plc client where type = dnp3 and
hostname = 127.0.0.1 and
port = 20001 and
master_id = 1 and
outstation_id = 10 and
frequency = 20 and
name = dnp3_dyn and
dbms = new_company and
dynamic = true and
map = [{"name":"analog_0","type":"Analog","index":0}]
>
Dynamic ingest with UNS (namespace + master_node)
With dynamic = true, you can add a Unified Namespace path and a master node so DNP3 ingest is registered in the UNS alongside your policies and DBMS. namespace requires master_node = [ip:port] for policy updates.
run plc client where type = dnp3 and
hostname = 127.0.0.1 and
port = 20001 and
master_id = 1 and
outstation_id = 10 and
frequency = 20 and
name = dnp3_uns and
dbms = new_company and
dynamic = true and
master_node = 192.168.1.88:32048 and
namespace = FA9/MID9/DEVICE9 and
map = [{"name":"analog_0","type":"Analog","index":0}]
Table names follow the same pattern as plain dynamic = true (client name plus map name, e.g. dnp3_uns_analog_0 for the example above). Under UNS, the read value is usually stored in a column named like the tag—the map name (here analog_0), not a generic value column.
With namespace, table and column layout follow UNS policies. namespace and master_node drive how tables are registered in the UNS. See Unified Namespace for background.
For a full example combining this with a scripted deployment and a reusable mapping policy (rather than an inline map array), see Deploying a DNP3 Connector via Script.
DNP3 Out Station Testing
The opendnp3 library includes a demo outstation for lab tests. Build it with demos enabled (TLS optional):
git clone --recursive -b release-2.x https://github.com/dnp3/opendnp3.git
cd opendnp3
mkdir build && cd build
cmake -DDNP3_DEMO=ON -DDNP3_TLS=ON .. # omit -DDNP3_TLS=ON for TCP-only
make -j
See the OpenDNP3 CMake guide and TLS support (OpenSSL ≥ 1.1.1 required for TLS).
Plain TCP outstation
From the opendnp3 build directory:
cd ~/opendnp3/build
./outstation-demo
The demo listens on any IP address, port 20001, outstation link id 10, and expects master link id 1. Use those values in AnyLog:
<get dnp3 values where type = dnp3 and
hostname = 127.0.0.1 and
port = 20001 and
master_id = 1 and
outstation_id = 10 and
map = [{"name":"analog_0","type":"Analog","index":0},
{"name":"binary_0","type":"Binary","index":0},
{"name":"counter_0","type":"Counter","index":0},
{"name":"doublebit_0","type":"DoubleBit","index":0}]
>
Once started, the demo logs traffic and waits for input to send unsolicited measurement changes:
Enter one or more measurement changes then press <enter>
c = counter, b = binary, d = doublebit, a = analog, o = octet string, 'quit' = exit
TLS outstation
Generate a test CA chain first — see DNP3 TLS Test Certificates for the full script and instructions. Run it in a working directory of your own, outside this repository:
mkdir -p ~/dnp3-tls-test && cd ~/dnp3-tls-test
bash create_certificates.sh
| Side | CA (peer) | Certificate | Private key |
|---|---|---|---|
| AnyLog master | anylogDNP3ca.cert |
master1.cert |
master1.key |
| Outstation | anylogDNP3ca.cert |
outstation1.cert |
outstation1.key |
From the opendnp3 build directory, start the TLS demo with three PEM paths (CA, outstation certificate, outstation private key):
cd ~/opendnp3/build
./outstation-tls-demo \
~/dnp3-tls-test/anylogDNP3ca.cert \
~/dnp3-tls-test/outstation1.cert \
~/dnp3-tls-test/outstation1.key
Same link ids and port as plain TCP (master_id = 1, outstation_id = 10, port 20001). AnyLog master uses the master certificate files from the same CA chain.
Example AnyLog one-shot read:
<get dnp3 values where type = dnp3 and
hostname = 127.0.0.1 and
port = 20001 and
master_id = 1 and
outstation_id = 10 and
enable_tls = true and
tls_ca = ~/dnp3-tls-test/anylogDNP3ca.cert and
tls_cert = ~/dnp3-tls-test/master1.cert and
tls_key = ~/dnp3-tls-test/master1.key and
map = [{"name":"analog_0","type":"Analog","index":0}]
>
More detail, including the full generation script: DNP3 TLS Test Certificates.
Third-party simulator
Another option is a commercial DNP3 outstation simulator, for example the FreyrSCADA DNP3 development bundle. After download, DNPOutstationSimulator.exe under the Simulator folder can be installed on Windows (32-bit).
Command keywords (summary)
| Keyword | Required / notes |
|---|---|
type |
dnp3 |
hostname, port |
DNP3 TCP/TLS target (default port 20000) |
master_id |
Master link address (default 1) |
outstation_id |
Outstation link address (default 10) |
frequency |
Poll interval (seconds) |
name |
Unique client name |
dbms |
Target DBMS |
table |
Wide-table ingest; omit with dynamic = true |
dynamic |
true for per-map tables or UNS |
map |
JSON array of points, inline or resolved from a mapping policy |
namespace |
UNS path (DNP3 + dynamic = true only) |
master_node |
Required when namespace is set |
enable_tls, tls_ca, tls_cert, tls_key |
Optional TLS (all three PEM paths required) |
Related
- Adding Data to Nodes in the Network
- Unified Namespace
- DNP3 TLS Test Certificates — generating a local CA chain for TLS testing
- Deploying a DNP3 Connector via Script — a production-style
.aldeployment script, parameters, and error handling - DNP3 Mapping Policies — reusing a point map across multiple connections instead of repeating it inline
- Modbus
- OPC-UA
- EtherNet/IP