---
name: ecu-firmware
description: >-
  Work with automotive ECU firmware dumps (chiptuning / прошивки ЭБУ) — ANY brand, ANY
  Bosch ECU family (ME17.x, MED17.x, EDC17, ME7.x, etc.). Use this whenever the task
  involves: reading/editing a .bin firmware dump; Bosch checksum validation or correction
  (CRC32 / ADD16 / "контрольная сумма" so PCMflash/the ECU accepts the file); activating
  or changing a feature (cruise control / круиз, Euro2 / евро-2, immo-off, rev limiter,
  speed limiter, pedal/throttle response, launch control); finding which byte controls a
  feature via donor-diff or landmark search; reverse-engineering ECU code in Ghidra
  (TriCore); comparing tuner calibrations (Paulus, ADACT, RSW, Ledokol, MMK, MotorMaster,
  Chelyaba…); organizing or downloading large firmware collections (mega.nz with IP
  rotation, Yandex.Disk) into a Brand/Model/CalibrationID/Category/Calibrator structure;
  or diagnosing whether a symptom (P0130, rich running, high consumption) is the tune vs
  hardware. Trigger even when the user only says "посмотри прошивку", "поправь чек-сумму",
  "активируй круиз", "разложи прошивки", or names a calibration ID like GTD-B46CQS3-C000 —
  don't wait for them to say "ECU firmware". Do NOT use for OBD live-data only tasks with
  no .bin involved, or for non-automotive binary patching.
---

# ECU Firmware (chiptuning) toolkit

Work on Bosch ECU firmware dumps safely and deterministically. The cardinal rule:
**never hand back a file that could brick the ECU.** Every edit is surgical (change only
what the task needs), every checksum is recomputed AND independently validated, and every
"this firmware will fix X" claim is checked against real data, not assumed.

## Golden rules (read first)

1. **Surgical edits only.** Diff your output against the *original* and confirm ONLY the
   intended bytes (+ checksum machinery) changed. The user's existing tune must survive
   byte-for-byte. A feature edit that also perturbs map regions is a bug.
2. **Two-tool checksum agreement before flashing.** Compute with `tools/bosch_checksum.py`,
   then independently confirm with `tools/medc17/`. Only a file both agree on is flashable.
3. **Don't guess — diff and reverse.** "Which byte enables X" is answered by donor-diff
   (a firmware with X on vs off) + Ghidra, never by a forum's claimed offset alone.
4. **Free and legal.** No cracked tools, no paywall bypass, no license-key transplanting.
   If a ready file is paid (e.g. ecudump.ru), say so and build it ourselves instead.
5. **Separate firmware from hardware.** Before blaming/masking a DTC in firmware, prove it's
   the tune: if a symptom persists across multiple different tunes (incl. a conservative one
   that doesn't touch the relevant maps), it's hardware/wiring — masking it hides a real fault.

## Capabilities & where to go

| Task | Start here |
|---|---|
| Validate / fix Bosch checksums after an edit | `references/bosch_checksums.md` + `tools/bosch_checksum.py` + `tools/medc17/` |
| Activate/find a feature byte (cruise, euro2, limiter…) | `references/feature_activation.md` + `tools/feature_finder.py` |
| Reverse-engineer ECU code (TriCore) | `references/ghidra_tricore.md` + `tools/ghidra/` |
| Organize a firmware collection | `references/collection_pipeline.md` + `tools/organize_firmware.py` |
| Download collections at scale (mega + yandex) | `references/collection_pipeline.md` + `tools/download_mega.sh`, `tools/ip_rotation_tunnel.sh`, `tools/collection_pipeline.py` |
| Compare tuner calibrations / advise | `references/calibrators.md` |

Read the relevant reference file before acting — they carry the hard-won specifics
(exact offsets, algorithm quirks, gotchas) that make the difference between a working
file and a brick.

## The core edit workflow (any feature, any Bosch ECU)

This is the loop that produced a validated cruise file from a tuned base without
disturbing the tune. Generalize it:

1. **Identify the byte(s).** Either donor-diff (get the same calibration with the feature
   ON and OFF, align by a landmark, see what differs — `tools/feature_finder.py diff`) or
   confirm in Ghidra (find the instruction that reads the value). Cross-check the value
   against MANY same-ECU-family donors — a value that's consistent across siblings is the
   right one; a one-off is suspect.
2. **Patch + recompute checksums.** Edit the bytes, then fix every affected block's CRC32
   and ADD16 with `tools/bosch_checksum.py` (it handles the GF(2) solve when the adjust
   dword sits mid-region before an epilog marker).
3. **Validate.** `python tools/medc17/main.py <file>` → expect all blocks VALID. Then diff
   vs the original and confirm ONLY intended bytes changed (feature + checksum dwords).
4. **Pre-flash sanity.** Same file size as the dump; calibration-ID string intact; tune
   regions and App-SW region untouched (unless the task is a code patch); checksum-adjust
   dwords changed but the DEADBEEF epilog marker preserved.
5. **Hand off with flashing notes.** State exactly what changed and how few bytes; remind:
   stable power, don't interrupt, the previously-flashed version proves the structure loads.

## Hard-won gotchas (these cost real debugging time)

- **Offsets shift per model/sub-calibration.** The same block sits at different absolute
  offsets across calibration IDs because preceding tables vary in length. Find values by
  **landmark** (a constant adjacent pattern), not a fixed offset. `feature_finder.py` does this.
- **A donor value can be wrong-family.** A cruise byte of `800E` came from a Rio 1.4 and did
  NOT activate cruise; the correct `8007` came from the engine/platform twin (i30) and matched
  176 same-ECU siblings. Match the donor to the actual hardware variant.
- **medc17 `--correct` is WIP and rewrites ~132 bytes** (variant-dataset bug). Never use it.
  Use it only to *validate*; correct with `bosch_checksum.py`.
- **Tuner files often have a non-standard epilog** (DEADBEEF shifted 4 bytes). medc17 may not
  parse the dataset block until you normalize structure to the stock layout — see
  `references/bosch_checksums.md`.
- **macOS TCC blocks ~/Downloads** ("Operation not permitted"). Work in a normal dir
  (~/Разработки/…); Desktop is usually writable, Downloads/Documents may not be.
- **Importing a long-running daemon script runs its main loop on import.** Guard daemons with
  `if __name__ == "__main__":` or `import` will silently spawn a second copy that races on
  shared scratch dirs (caused phantom "0 files" failures in the pipeline).

## Setup notes

- medc17 is bundled in `tools/medc17/` (MIT, ConnorHowell). Run `python tools/medc17/main.py <bin>`.
- `tools/bosch_checksum.py` is standalone (pure Python, no deps).
- Ghidra (with built-in TriCore) + JDK21 are external — see `references/ghidra_tricore.md` for
  the headless import/analyze commands and the bundled analysis scripts in `tools/ghidra/`.
- Collection pipeline needs `megatools` (megadl), `unar`, `7z`, `rclone`, and (for IP rotation)
  an xray/v2ray SOCKS proxy with a rotating outbound — see `references/collection_pipeline.md`.
