Skip to main content

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)

Source distribution for dnp3-outstation 0.2.0
File Size Uploaded
dnp3_outstation-0.2.0.tar.gz 17.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dnp3-outstation 0.2.0
File Interpreter ABI Platform
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}

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page