Skip to main content

Python-based ECU emulation and automotive diagnostic tooling.

Project description

ECUmulator.py

Local TCP note: For loopback adapter connections in this project, use 127.0.0.1 instead of localhost. localhost can resolve to IPv6 (::1) while current services listen on IPv4 only.

Python-based ECU emulation + adapter tooling.

This repository hosts the Python tooling formerly living in the Swift ECUmulator repo:

  • pymulator — ECU emulator and CAN bus bridge
  • ELMpythonator — ELM327/STN emulator
  • hsfzclient — BMW HSFZ REPL client
  • doipclient — DoIP (ISO 13400) REPL client
  • epy — wrapper script to run server/client tools

This repo is Python‑only. Swift sources live elsewhere.

Installation

Homebrew (macOS, recommended for end users):

brew tap mickeyl/formulae
brew install ecumulator-py

Note: the Homebrew formula pulls source from a private GitLab repo over SSH. You need access to that repo and a configured SSH key (or a tokenized HTTPS URL).

pipx (isolated CLI install, recommended for developers):

pipx install git+ssh://git@gitlab.com/a11086/ecumulator.py.git

pip (venv or local checkout):

python3.12 -m pip install -r requirements.txt

Or install as a package (local checkout):

python3.12 -m pip install .

Install directly from GitLab (private repo):

pip install git+ssh://git@gitlab.com/a11086/ecumulator.py.git
# or with token-based HTTPS:
pip install git+https://<TOKEN>@gitlab.com/a11086/ecumulator.py.git

Which installer should I use?

  • Homebrew: most predictable for macOS end users and supportable fleet installs.
  • pipx: clean CLI install for devs without touching system Python.
  • pip/venv: best for local development or editable installs.

Optional dependencies are auto‑checked:

  • python-can (required for --attach)
  • gs_usb backend for python-can when using gs_usb (installed automatically on macOS via env marker; manual fallback: pip install gs_usb)
  • lz4 (required for --ecuconnect)

Non‑optional dependency:

  • zeroconf (required for ECUconnect logger discovery; now a hard dependency for --logger)

Tests

Recommended (PEP 668 friendly) workflow:

python3.14 -m venv .venv
.venv/bin/python -m pip install -U pip
.venv/bin/python -m pip install -r requirements-dev.txt
.venv/bin/python -m pip install -e .[dev]
.venv/bin/python -m pytest

Quickstart

Run with a spec:

epy server ecu:./path/to/spec.json

Interactive REPL:

epy server -i ecu:./path/to/spec.json

Attach to real CAN:

epy server -a can0@500000 ecu:./path/to/spec.json

Backend selection for --attach:

  • Linux defaults to socketcan.
  • macOS defaults to gs_usb.
  • Override explicitly with socketcan:<channel>@<bitrate>, gs_usb:<index>@<bitrate>, or toucan:<index>@<bitrate>.
  • TouCAN on macOS supports classic CAN bitrates: 10000, 20000, 50000, 100000, 125000, 250000, 500000, 800000, 1000000.

macOS gs_usb compatibility note:

  • ECUmulator applies a runtime shim for Python gs_usb startup to avoid macOS permission issues seen with reset/detach in some setups.
  • Disable this shim only for troubleshooting with PYMULATOR_GS_USB_MACOS_SHIM=0.

python-can-candle status note (February 2026):

UI (ECUmulator Studio)

The desktop UI lives in ui/ (Tauri + Vite). To run the dev build:

cd ui
npm install
npm run tauri dev

Building for Linux

Standard build (requires internet on first launch for Python runtime setup):

make linux

Offline .deb (bundles Python runtime + all wheels — no internet needed on target):

make deb

Install:

sudo apt install ./dist/ecumulator-studio_*.deb

Uninstall:

sudo apt remove ecumulator-studio

UI source structure (for contributors):

  • ui/src/App.tsx — root orchestration/composition
  • ui/src/app/defaults.ts — default state/constants
  • ui/src/app/types.ts — App-local types
  • ui/src/app/helpers.ts — pure helpers and formatting logic
  • ui/src/app/serviceCatalog.tsx — service/settings catalogs
  • ui/src/components/app/ — App-scoped components (modals, badges, quick-help window)
  • docs/ui-semantic-color-palette.md — semantic UI color tokens/classes and usage rules

When adding features, prefer extracting focused modules/components rather than expanding App.tsx.

Global Actions (Firewall ECUs)

In ECU Detail, the Global Actions firewall controls are only shown when the ECU role resolves to FIREWALL.

  • Matching is spelling-tolerant and case-insensitive.
  • Examples that match: FIREWALL, firewall, Fire-Wall, fire wall, fire_wall.
  • These controls are shown for UDS/KWP ECUs and map to ECU startupActions (lockAllOtherECUs / unlockAllOtherECUs).

Flow Control Testing (ISO-TP)

Use these CLI options to inject realistic flow-control stalls and overruns for tester validation.

Occasional short stall (10% of FCs, 2 WAIT frames, 25ms spacing):

epy server ecu:./path/to/spec.json --isotp-fc-wait-prob 0.1 --isotp-fc-wait-count 2 --isotp-fc-wait-ms 25

Rare overrun (1% of FCs), deterministic:

epy server ecu:./path/to/spec.json --isotp-fc-overflow-prob 0.01 --isotp-fc-rng-seed 42

Forced overrun (every FC is OVERFLOW):

epy server ecu:./path/to/spec.json --isotp-fc-overflow

CAN FD + ISO-TP/FD

Transport profile is controlled per vehicle via bus.transportProfile:

  • "isotp" (default): classic CAN framing.
  • "isotp_fd": CAN FD + ISO-TP/FD framing.

When transportProfile is isotp_fd, FD behavior is enabled for:

  • Internal CAN runtime transport.
  • External CAN bridge paths.
  • ECUconnect FD channels (open_fd_channel, raw_fd, isotp_fd).

Compatibility note:

  • HSFZ, DoIP, and ELM327/STN2xxx are classic-transport services and are unavailable with transportProfile: "isotp_fd".
  • Studio disables those service toggles automatically under isotp_fd.
  • Sidecar also rejects invalid combinations at apply/runtime for safety.

Supported Commands

epy command list:

  • server [pymulator args...] — ECU emulator + CAN bus bridge.
  • import [specimport args...] — Generate vehicle/ECU JSON specs from candump, CL1000, ECUconnect, or PCAP/PCAPNG logs.
  • spec compile <vehicle-or-project-dir> [-o out.vehicle] [--overwrite] — Build canonical standalone .vehicle.
  • spec explode <vehicle-or-project-dir> [-o out-dir] [--overwrite] — Create project layout (vehicle.vehicle + ecus/*.json).
  • client --doip [doipclient args...] — DoIP REPL client.
  • client --hsfz [hsfzclient args...] — HSFZ REPL client.
  • elm [elmpythonator args...] / elmpythonator [args...] — deprecated; use server --elm327.
  • update [--force] — Check Git tags and update the installed package.
  • version — Print current version.
  • help / --help — Show usage.

Example:

epy import ./capture.log -o ./specs

Sample Specs

The specs/can/ directory includes CAN-based OBD2 fixtures for app testing:

epy server specs/can/obd2-can29-vehicle.json

Recommended for new vehicle specs: use the .vehicle extension. Legacy .json vehicle wrappers are still supported.

That vehicle spec loads two ECUs:

  • specs/can/obd2-can29-ecu-a.json (powertrain)
  • specs/can/obd2-can29-ecu-b.json (transmission/secondary)

CAN FD demo fixture (11-bit arbitration + ISO-TP/FD):

epy server specs/can/canfd-can11-vehicle.json

ECUconnect TCP adapter (optional)

Expose ECUconnect/CANyonero compatible TCP server:

epy server --ecuconnect ecu:./path/to/spec.json

Combine with a real bus:

epy server --ecuconnect -a can0@500000 ecu:./path/to/spec.json

Behavior notes:

  • Adapter identity (request_info firmware/version + hardware revision) is configurable in ECUmulator Studio Settings.
  • Virtual firmware update PDUs are supported (prepare_for_update, send_update_data, commit_update, reset) with real-world timing simulation (3s prepare, 50ms write chunk, 2s commit).
  • On commit_update, the uploaded image is scanned for a firmware version string. On reset, the adapter applies it and restarts.
  • The learned version is persisted in Studio service settings (ecuconnectVersionFromUpdate + ecuconnectVersionUpdatedAt) and survives Studio restarts.

Protocol coverage details: docs/ecuconnect.md.

ECUconnect Logger (optional)

Stream all CAN traffic in the ECUconnect Logger binary format over TCP and advertise via Zeroconf:

epy server --logger ecu:./path/to/spec.json

Custom service name:

epy server --logger --logger-name "ECUmulator.py Lab" ecu:./path/to/spec.json

HSFZ TCP adapter + UDP discovery (optional)

epy server --hsfz ecu:./path/to/spec.json

Requires classic transport profile (bus.transportProfile: "isotp").

Protocol coverage details: docs/hsfz.md.

DoIP (ISO 13400) TCP adapter + UDP discovery (optional)

epy server --doip ecu:./path/to/spec.json

Requires classic transport profile (bus.transportProfile: "isotp").

Protocol coverage details: docs/doip.md.

ELM327/STN2xxx TCP adapter (optional)

epy server --elm327 ecu:./path/to/spec.json

Requires classic transport profile (bus.transportProfile: "isotp").

Interpreter details: docs/ELM327-STN-COMMANDS.md.

ENET Service Broker vehicle bridge (optional)

ECUmulator can register as the vehicle endpoint against an ENET Service Broker and forward CAN frames over the broker TCP stream:

epy server --enet-service-broker --enet-service-broker-vin WBADE6324VBW12345

Optional broker URL override:

epy server --enet-service-broker --enet-service-broker-vin WBADE6324VBW12345 --enet-service-broker-url https://esb.cornucopia.dev

ISO-TP mode:

epy server --enet-service-broker --enet-service-broker-vin WBADE6324VBW12345 --enet-service-broker-mode isotp

Notes:

  • VIN is required (17 characters).
  • By default, broker discovery uses Zeroconf service enet-service-broker._enetbroker._tcp on the local network.
  • --enet-service-broker-url is only needed to force a specific broker endpoint.
  • The broker response includes a tester port (vehicle port + 1000).
  • Transport mode can be isotp (default) or raw.
  • Studio shows the currently allocated tester port under the ENET service badge.

Clients

HSFZ REPL client:

epy client --hsfz

DoIP REPL client:

epy client --doip

Both clients accept explicit URLs:

epy client --hsfz enet://127.0.0.1:6801/en0
epy client --doip doip://127.0.0.1:13400/en0

ELM327/STN2xxx interpreter (ELMpythonator)

The ELM327/STN2xxx interpreter now runs inside the TCP adapter service (not as a standalone CLI).

epy server --elm327 ecu:./path/to/spec.json

Optional flags:

epy server --elm327 --elm327-host 0.0.0.0 --elm327-port 35000 --elm327-name "ELM327/STN2xxx"

Connect with a TCP client (telnet, netcat, or your app) and issue AT/ST commands. See docs/ELM327-STN-COMMANDS.md for the full command list and behavior.

Updates

epy checks once a day for newer Git tags and offers to update. You can force an update check:

epy update

If your Python is externally managed (PEP 668), auto‑updates are disabled to avoid breaking system packages. Use a virtualenv or pipx, or force a system update:

epy update --force

If you installed via Homebrew, epy update is disabled and will tell you to use:

brew update
brew upgrade ecumulator-py

Notes

  • Use --bus-timing-factor to simulate CAN timing for virtual buses.
  • Sample OBD2 CAN29 specs live in specs/. For additional scenarios, point to your local specs repo.
  • Broadcast discovery is minimal and answers first response received.

Protocol Overviews

  • docs/ecuconnect.md — ECUconnect adapter + logger support matrix.
  • docs/hsfz.md — HSFZ TCP/UDP support notes.
  • docs/doip.md — DoIP UDP discovery + TCP diagnostic coverage.

Supported Services

Below is a quick reference for supported diagnostic services and subcommands in this Python implementation.

UDS (ISO 14229)

Service Name Supported subcommands / notes
0x10 Diagnostic Session Control Supports standard session types (by enum).
0x11 ECU Reset Standard reset types (by enum).
0x14 Clear Diagnostic Information Only groupOfDTC = 0xFFFFFF.
0x19 Read DTC Information Only report DTC by status mask (subfunction 0x02).
0x22 Read Data By Identifier Full DID list; supports multiple DIDs in one request.
0x23 Read Memory By Address Reads from memory blocks when mapped; otherwise filler 0xFF.
0x24 Read Scaling Data By Identifier Same data as Read DID (minimal implementation).
0x27 Security Access Seed/key pairs from spec; odd/even and vendor levels.
0x28 Communication Control Accepted; no-op response.
0x2A Read Data By Periodic Identifier Acknowledges with transmissionMode + echo DIDs.
0x2C Dynamically Define Data Identifier Define-by-identifier, define-by-memory, clear.
0x2E Write Data By Identifier Updates DID content.
0x2F Input/Output Control By Identifier Echoes control record; updates DID if provided.
0x31 Routine Control Start/stop/results based on spec routines.
0x34 Request Download Memory-block based.
0x35 Request Upload Memory-block based.
0x36 Transfer Data Block sequence handling.
0x37 Request Transfer Exit Completes active transfer.
0x3D Write Memory By Address Writes to memory blocks.
0x3E Tester Present Standard 0x00/0x80 plus VehiCAL extensions (0x30/0x32/0x33).
0x85 Control DTC Setting Accepted; no-op response.
Custom customServices matcher Spec-defined strict/prefix/suffix handlers with fixed/replay responses and optional actions.

KWP2000 (ISO 14230)

Service Name Supported subcommands / notes
0x10 Start Diagnostic Session Standard session types (by enum).
0x11 ECU Reset Standard reset types (by enum).
0x12 Read Freeze Frame Data Returns empty positive response.
0x13 Read Diagnostic Trouble Codes Returns all DTCs (count + 3-byte DTCs).
0x14 Clear Diagnostic Information Only groupOfDTC = 0xFFFF.
0x17 Read Status Of DTCs Status mask filtering, returns status + DTCs.
0x18 Read DTCs By Status Only subfunction 0x02 with mask FFFF.
0x1A Read ECU Identification ECU ID table via spec (incl. VIN if provided).
0x20 Stop Diagnostic Session Resets to default session.
0x21 Read Data By Local Identifier Local identifier table.
0x22 Read Data By Common Identifier Common identifier table.
0x23 Read Memory By Address 3-byte address + 1-byte length.
0x24 Read Scaling Data By Identifier Same data as Read Common ID.
0x27 Security Access Seed/key pairs from spec; odd/even and vendor levels.
0x2C Dynamically Define Data Identifier Define-by-identifier, define-by-memory, clear.
0x2E Write Data By Identifier Writes common identifiers.
0x31 Routine Control Start/stop/results based on spec routines.
0x34 Request Download Memory-block based.
0x36 Transfer Data Block sequence handling.
0x37 Request Transfer Exit Completes active transfer.
0x3E Tester Present KWP-style (no subfunction).
0x81 Start Communication Echoes payload.
0x82 Stop Communication Echoes payload.
0x83 Access Timing Parameters Echoes payload.
0x84 Network Configuration Echoes payload.
Custom customServices matcher Spec-defined strict/prefix/suffix handlers with fixed/replay responses and optional actions.

OBD-II (SAE J1979, CAN/ISO-TP)

Mode Name Supported subcommands / notes
0x01 Show Current Data PID support bitmaps + configured PIDs.
0x02 Show Freeze Frame Data Uses frame index + PID.
0x03 Show Stored DTCs Count + two-byte DTCs.
0x04 Clear DTCs Clears stored + pending DTCs.
0x05 O2 Sensor Monitoring PID support bitmaps + configured PIDs.
0x06 On-board Monitoring PID support bitmaps + configured PIDs.
0x07 Show Pending DTCs Count + two-byte DTCs.
0x08 Control On-board System/Test PID support bitmaps + configured PIDs (controlOperations).
0x09 Vehicle Information PID support bitmaps + configured PIDs.
0x0A Permanent DTCs Count + two-byte DTCs.

License

Proprietary

Contact

Dr. Michael 'Mickey' Lauer — mickey@vanille-media.de

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ecumulator_py-0.9.211.tar.gz (348.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ecumulator_py-0.9.211-py3-none-any.whl (412.4 kB view details)

Uploaded Python 3

File details

Details for the file ecumulator_py-0.9.211.tar.gz.

File metadata

  • Download URL: ecumulator_py-0.9.211.tar.gz
  • Upload date:
  • Size: 348.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for ecumulator_py-0.9.211.tar.gz
Algorithm Hash digest
SHA256 df5bdde782a3dfa59ac5082ce50bcf55531d61a55e4ce894fd72bd7276c2d47c
MD5 5f9d90f48ffdb863883d07cf9a281377
BLAKE2b-256 eccaaa80f23ef50e6c5a273c18eab4ccd2332aaabebd9818f3d901171d017aaf

See more details on using hashes here.

File details

Details for the file ecumulator_py-0.9.211-py3-none-any.whl.

File metadata

File hashes

Hashes for ecumulator_py-0.9.211-py3-none-any.whl
Algorithm Hash digest
SHA256 d95b3dc18234ca82f2b4937a71c1b68c8060173d96fb45a71e6388d32ca6504b
MD5 e2387fcaee50f2f33150c953034c3916
BLAKE2b-256 d86cdadce466821a3b9b117f3804f4b88a55d585828f72f260cb12dedd0292a3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page