dnp3-outstation 🐍
Pure-Python DNP3 outstation (IEEE 1815-2012, Level 1 subset).
aarch64-compatible drop-in replacement for the outstation half of
dnp3-python, which ships
x86_64-only wheels.
Scope (and what's deliberately not in)
✅ One TCP outstation per process, asyncio-native
✅ Group 30 / variation 5 analog inputs (32-bit float with flag byte)
✅ READ requests with qualifier 0x06 (class-0 / integrity poll) and qualifier 0x00 (8-bit range)
✅ Responses encoded as one g30v5 qualifier-0x00 header per contiguous index run (rust dnp3 + opendnp3 accept this)
✅ Datalink REQUEST_LINK_STATUS → LINK_STATUS, RESET_LINK_STATES → ACK
✅ WRITE (FC=0x02) ACKed -- master can clear IIN1.RESTART
✅ IIN1.RESTART on cold start, self-clears after first response
✅ Interop verified against the rust dnp3 v1.6 master crate (same one used by ems-industrial-gateway)
❌ Master mode ❌ Secure authentication (SAv5/SAv6) ❌ File transfer, time sync, control output ❌ Event classes 1/2/3, change-driven reporting ❌ Function codes beyond READ (0x01) and WRITE (0x02)
Anything missing comes from the same place: this lib exists to publish a small, fixed set of analog values to a polling master. That's it.
Install
pip install dnp3-outstation
Python 3.11+, pure-Python — works on any platform Python runs on (Raspberry Pi 5 aarch64 included).
Usage
import asyncio
from dnp3_outstation import AsyncOutstation
async def main():
outstation = AsyncOutstation(
host="0.0.0.0",
port=20000,
master_addr=1,
outstation_addr=1024,
)
outstation.set_analog(idx=0, value=1_000_000.0)
outstation.set_analog(idx=1, value=2_000_000.0)
outstation.set_analog(idx=10, value=250.0)
await outstation.start()
try:
while True:
# update values on whatever cadence makes sense
outstation.set_analog(idx=10, value=read_my_sensor())
await asyncio.sleep(2)
finally:
await outstation.shutdown()
asyncio.run(main())
set_analog(idx, value, flag=0x01) updates the in-memory point database.
The next master poll returns whatever's there. Flag byte defaults to
0x01 (ONLINE) per the DNP3 analog-input flag definition.
Architecture
Layered to mirror IEEE 1815's stack so each layer tests independently:
AsyncOutstation asyncio TCP server, lifecycle
|
Outstation state + orchestration (sync)
|
application.py AC/FC/IIN, g30v5 encoding, READ parsing
|
transport.py FIN/FIR/SEQ segment header
|
datalink.py SYNC + LEN + CTRL + addresses + CRC blocks
|
crc.py CRC-16/DNP per spec section 11.7
Every layer has colocated unit tests under src/dnp3_outstation/test_*.py.
License
MIT.
Release files for dnp3-outstation 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dnp3_outstation-0.2.0.tar.gz | 17.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dnp3_outstation-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 42.4 kB
Release files / dnp3_outstation-0.2.0.tar.gz
| Download URL | dnp3_outstation-0.2.0.tar.gz |
|---|---|
| Size | 17.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9ce59cfed4b05c0d06db9ff02d683aa65272f409dbe6c78e53db65ddcf5736f9
|
|
BLAKE2b-256 checksum How to use checksums |
7f34cb829526c4050c85b991fafcd25a87b6c0b0c2aed5626eb06e667177b6b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / dnp3_outstation-0.2.0-py3-none-any.whl
| Download URL | dnp3_outstation-0.2.0-py3-none-any.whl |
|---|---|
| Size | 24.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ffd7e8cfef9001f4510a6e92d2d9e6a9b28b4f693dbb0d0c877093454feee66e
|
|
BLAKE2b-256 checksum How to use checksums |
df7522f52d92b77e480715948d96dff909ee4347b698dca2024aaa7eb54cb5f0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|