# Tuning the 2010 T100 — master reference One place that answers "how do we actually tune this bike, any way we want" — indexes everything scattered across this project's research files into the actual decision tree: what's possible, what's proven, what's still open. Read this first; follow the links for the technical depth behind each claim. **The bike:** 2010 Triumph Bonneville T100, 865cc air-cooled twin, Keihin ECU (Renesas SH7054), **mechanical odometer** — this generation detail matters constantly (see "the one recurring trap" below). **Current real-world state:** SAI plumbing physically removed, solenoid left electrically connected (clicking, harmless — [resistor fix below](#stop-the-sai-solenoid-clicking)). Exhaust: Arrow 2-in-1 in hand, not yet fitted. Airbox: still stock, removal planned for later. Fuel: Canadian premium, ≤10% ethanol (E10). ECU not yet connected to (`tunie info` never run) — ["Phase 1" in the project's own `../STATUS.md`](../STATUS.md), still blocked on the Triumph diagnostic connector cable. ## Two goals, and they sometimes pull in different directions **Primary:** tune my own bike correctly for whatever mods actually go on it — exhaust now, airbox later, whatever comes after that. Accuracy and correctness win when this goal is in tension with the one below. **Secondary:** make the tuning process non-invasive and simple enough that this gets open-sourced and other riders can use these tools without cutting/drilling/welding anything on their bike they can't easily undo. These aren't always aligned, and it's worth being explicit about it rather than letting decisions quietly optimize for one without saying so: - The bung-reuse / device-flag-decoupling approach two sections down is a case where both goals point the same way — no permanent modification *and* it doesn't compromise the primary tune's correctness. When that's possible, it's the obvious choice. - Elsewhere they can conflict: e.g. two wideband sensors (one per cylinder, pre-collector) gives better tuning accuracy (goal 1) but costs more hardware and setup complexity than one sensor post-collector (goal 2). No blanket rule — flag the trade-off explicitly at each decision point rather than silently picking one goal's answer. - The open-sourcing goal also means the eventual tooling (Phase 0/4 in `TUNING_IMPL_PLAN.md`) should stay agnostic to *which* hardware path a given rider picked, rather than hard-coding this build's specific choices — already the design intent there, worth keeping true as it gets built out. --- ## Is the no-drilling ECU-streamed plan actually achievable? (Aug 2026 status check) Short answer: **architecturally yes, nothing found so far says it can't work — but it is not yet proven end-to-end, and it all sits behind the one blocker this whole project has had since before this plan existed.** Broken down honestly, piece by piece: | Piece | Status | |---|---| | No drilling — reuse stock O2 bungs for wideband | **Solid.** M18x1.5 thread standard confirmed for both sensor types and this Triumph twin family specifically. | | Wideband sensor + controller hardware | **Solid.** Off-the-shelf, well-understood (Innovate/AEM), just needs buying and wiring — execution risk only, no research risk. | | ECU exposes RPM/TPS/coolant live | **Mechanism confirmed**, specific record IDs **not yet confirmed for this bike**. Recovered one candidate record array from the decompile (the Keihin-family "default" case), but which `Oe()` switch case actually applies to a Bonneville twin specifically hasn't been traced through the ECU-type-selection logic — it's a good candidate, not a verified one. | | Wideband AFR into the onboard logger | **Solid**, standard ADC-reads-a-voltage problem, no open questions. | | TuneECU's F Trim / Commit trims workflow | **Confirmed from documentation** (`android.html`), never operated hands-on — no bike has been connected yet, project-wide, per `../STATUS.md`. | | Mapping log points to fuel-table cells | **Solid** — RPM/throttle axes already extracted and validated (`reference-maps/table_map.py`). | | Polling-loop + onboard-unit integration code | **Not written yet.** Phase 0 of `TUNING_IMPL_PLAN.md` — straightforward extension of `tunie`'s existing transport, but zero lines of it exist right now. | **The one hard blocker underneath all of it:** first ECU contact (`tunie info`) has never happened — still stuck on the Triumph diagnostic connector adapter, the same blocker `../STATUS.md` has tracked since before this tuning plan existed. Record labeling, live-capture validation, and confirming the F Trim workflow hands-on all require that connection to exist first. Nothing about the plan is contradicted or looks unworkable — it's just genuinely unverified past the paper-architecture stage until that cable problem gets solved. **So: achievable, yes. Achieved, not yet.** The path from here to "working system" is: solve the cable → `tunie info` (resolves current stock map ID as a side effect) → label the live records empirically → write the polling-loop code (can actually start before the cable's solved, against mocked data) → wire the wideband ADC → do a real capture → build the first tune. No step in that chain currently looks like it won't work — but none of them past "the cable exists" have actually been tried. --- ## The five ways to get a tune, ranked by effort/reliability ### 1. Pick an existing official TuneECU calibration (done, free, safest) For Arrow 2-in-1 + E10 + mechanical odo, TuneECU's own catalogue already has the validated answer: **map `20262` or `20313`** (same tune, two catalogue reissues — see [`reference-maps/PERMUTATIONS.md`](reference-maps/PERMUTATIONS.md) for the proof they're identical bar one trivial byte). This is real engineering data from Triumph/TuneECU, dyno-validated by them, zero DIY risk. Covers exhaust + fuel type. **Does not cover SAI/O2 delete or airbox removal** — TuneECU never ships those combined with an aftermarket exhaust, confirmed by an exhaustive catalogue-wide keyword search (zero hits across all 1811 entries, any model) — see [`COMMUNITY_TUNING.md`](COMMUNITY_TUNING.md#existing-tunes-for-airbox-removal--2-1-exhaust-aug-2026). ### 2. Flip the SAI/O2 device flags on top of #1 (done, free, low risk) `reference-maps/toggle_devices.py` flips the three device-enable bytes (`0x53801` SAI, `0x53818`/`0x53819` O2×2 — found and independently re-verified 3× across different reference maps, see [`reference-maps/DEVICES.md`](reference-maps/DEVICES.md)) on any base map. Already built: `reference-maps/derived/20262-noSAI-only.hex` (matches your resistor-swap plan — see below) and `...-noSAI-noO2.hex`. Confirmed byte-for-byte disjoint from the Arrow exhaust calibration, so this composition is sound, not a guess ([`COMMUNITY_TUNING.md`](COMMUNITY_TUNING.md#what-the-real-device-flag-file-tells-us-about-composing-arrow--delete)). **Important, established this session:** this flag most likely only **suppresses the fault-code/CEL logic** for a missing device — it does *not* reliably change fueling behavior. Multiple forum sources describe it this way, and TuneECU's own current Android UI still separately exposes "Idle Fuel Trim (CO)" as *the* parameter specifically for "Triumph without O²-Sensor" — i.e. the flag and the fueling correction are two different things you both need, not one flag that does both. ### 3. Hand-compose a "good enough" trim correction (done, updated Aug 2026) `reference-maps/compose_arrow_delete.py` sets **both** idle-trim bytes now (`0x536AB` and `0x5369C`, part of the 32-entry "Idle Fuel Trim (CO)" table, [`reference-maps/TABLES.md`](reference-maps/TABLES.md#fuelignition-trim-array--rpm-indexed-32-entries-0x53690-0x536af)) — `0x5369C` was originally excluded as "exhaust-family-dependent," but that was an artifact of reading the byte as unsigned; the table is signed, and in signed space both bytes compose the same way (one saturates at `+127`). **This composition is still scoped to the SAI/O2-delete case only — it is explicitly NOT an airbox-removal tune.** A real dyno reference on the same ECU family (`20184dynoTuneSteveO2-Disable.hex`) showed a real airbox+open-pipe mod rewrites nearly the *entire* 32-cell trim table plus a further ~50-byte region we haven't mapped — see [`COMMUNITY_TUNING.md`](COMMUNITY_TUNING.md#major-find-a-real-dyno-tune-on-our-exact-ecu-generation-airbox-removed-aug-2026). Output: `reference-maps/derived/20262-arrow-delete-composed.hex`. ### 4. Buy a matched commercial tune (real, currently blocked) - **Triumph Twin Power (TTP)** had exact-match products (`Tune 11 2-1`: airbox removal + 2-1 exhaust + O2/SAI removal) — but the storefront is effectively wound down (founder retirement 2022, every SKU checked shows "not in stock," hardware supply handed to a sister company). Don't count on this. - **DNK TuneWorks** ($275, mail-in or self-flash) — no airbox-specific variant listed for the 865 air-cooled model, but they've built per-mod variants off-catalogue before (per a customer review); worth emailing directly. Watch for their *other*, wrong-model product page (liquid-cooled 2016+ T100) — easy to mix up. - Full details: [`COMMUNITY_TUNING.md`](COMMUNITY_TUNING.md#existing-tunes-for-airbox-removal--2-1-exhaust-aug-2026). ### 5. Road-tune it yourself, no dyno (documented, not yet tried, most promising for airbox) A complete, concrete methodology exists and is **confirmed still usable in the current TuneECU Android app** (not a dead Windows-only technique): wideband O2 sensor + datalogger, drive fixed throttle positions through the RPM range, log target-vs-measured AFR, feed the discrepancy into the **F Trim** table (swipe right from the F/I table screen), **Commit trims** (bakes correction into the permanent fuel table, resets trim to zero), repeat 3-4 passes. A dyno session afterward becomes optional fine-tuning, not a requirement. Full writeup, sourced: [`COMMUNITY_TUNING.md`](COMMUNITY_TUNING.md#major-find-a-documented-no-dyno-road-tuning-method-aug-2026). **This is the actual answer to "how do we get airbox removal tuned without paying a tuner."** It directly fixes what airbox removal breaks (the fuel table no longer matching real airflow) using measured reality, sidestepping the whole byte-composition problem in #3 entirely. #### How it actually works, mechanically The ECU's fuel table (`FuelMap`) stores intake air mass in mg for each RPM/throttle-position cell. Hardware changes (exhaust, airbox) change the real airflow at each cell without changing what's stored — the table goes stale until someone re-measures it. A dyno does that by holding the bike at load and reading a wideband sensor; this method does the same measurement on the road. 1. **Get a truthful AFR reading.** The stock O2 sensor is narrowband — fine for a 14.7:1 closed-loop target, useless for tuning richer WOT mixtures. A wideband sensor + controller reads accurately across the whole range actually being tuned (roughly 12.5-14.7:1). 2. **Flatten the baseline so it can't lie.** Before measuring, load a tune with AF1/AF2 (target-AFR tables) both set flat to something safely rich (13.0:1 in the original guide) and **F Trim** zeroed. This isolates "is the fuel table wrong" from "was the target already weird here" — you can't tell those apart if the target varies cell-to-cell too. 3. **Log real riding data.** Mark the throttle grip at the fuel table's throttle breakpoints. On a quiet straight road, hold each mark steady, sweep the RPM range, three passes per mark. Log RPM, table row/cell, and measured-vs-target AFR at each point. 4. **Translate the log into corrections.** For each logged point, work out the nearest fuel-table cell (interpolating between throttle breakpoints) and how far off the AFR was — that error goes into the **F Trim** table, a temporary zero-baseline overlay the same shape as the main table. 5. **Commit and iterate.** **Commit trims** permanently bakes the F Trim corrections into the real fuel table and zeroes F Trim again. Reflash, repeat the same test. Converges in roughly 3-4 passes per the original author. 6. **Finish it.** Once the base table is correct, replace the flat 13.0:1 placeholder with real AF1/AF2 targets (richer at full load for power, leaner at part load for drivability/economy), one final commit. 7. **Dyno becomes optional** — at that point it's just fine-tuning via F Trim on an already-correct base map, not the whole job. #### Tools actually required — and why this isn't a simple process Being honest about this rather than making it sound tidier than it is: **Fabrication.** The wideband sensor needs a bung in the exhaust — either weld one in (needs a welder, or a shop that will), or find/reuse an existing bung (check whether Arrow's collector has a provision, or whether the stock O2 bung is reusable). Not a bolt-on step. **Electrical work.** Wideband controller needs switched 12V, ground, and the sensor's connector wired in — same category of work as the SAI resistor swap, just for a different, more sensitive sensor (wideband sensors are also consumables that die if run rich/unheated wrong, adding a "don't fry the $80 sensor" constraint the resistor job didn't have). **A real integration gap, not just extra steps.** The German guide's slick workflow — a single log row giving you table position *and* measured AFR together, entered with one keypress — depended on a **TuneBoy-specific cable with a built-in analog input**, a different product from TuneECU. **It is not confirmed that TuneECU's cable has an equivalent.** Without it, this becomes correlating *two separate logs* (the wideband's own recording, and whatever position/RPM reference you have) after the fact, by eye/timestamp — meaningfully messier and more error-prone than the streamlined description implies. This is the single biggest unresolved question before treating this as a smooth process: does TuneECU support synced logging at all, or does every pass require manual correlation. **A second software tool.** The wideband controller has its own logging software/app (Innovate LogWorks, AEM's app, or onboard SD), separate from TuneECU. Plus a spreadsheet to do the cell-correlation math by hand. **Riding logistics, not just equipment.** Steady, repeatable throttle positions at speed, on a quiet legal road, while a logger runs — solo, that's watching a gauge/throttle marks/traffic/road all at once, which is a real safety concern, not a paperwork one. A passenger to run the logger, or a fully autonomous SD-logging setup that needs zero mid-ride interaction, is the practical answer, not "just wear the gauge and go." **Time.** Multiple ride-then-reflash cycles (3-4 minimum, per the guide), each needing the bike, the logger, a suitable road, and a laptop/Android device to reflash between passes. Realistically spread across more than one outing, not an afternoon. **Net honest assessment:** this is a real DIY-tuning project — fabrication + wiring + a second logging tool + an unresolved software-integration question + genuine riding-safety logistics + several iteration cycles — not a "flash a file and go" task. It's the most *correct* path (measures your actual bike, not someone else's), but it's the highest-effort of the five options in this document, not the easy one. Worth weighing again against just paying DNK once/if they confirm an airbox variant (#4) — that trades money for skipping all of the above. --- ## Better idea (Aug 2026): stream live data straight off the ECU with `tunie` The messy "two separate logs correlated by hand" problem above assumed we needed TuneECU's app/cable for the ECU side. We don't — `tunie` already has verified, safe, read-only K-line access to this exact bike. Its own live dashboard is built on the same mechanism, confirmed in the decompile: - **Service `0x21` ReadDataByLocalIdentifier**, already implemented in `tunie` (`src/tunie/kwp2000.py`, used today by `identify.py`'s one-shot sweep) — same service TuneECU's live dashboard polls continuously via a round-robin scheduler (`m.java`'s `se()`/`Oe()` functions). - Records are **2-byte extended identifiers** (not the single-byte ones `identify.py` currently sweeps), sent as `0x21 `. Recovered the actual per-model record table for the Keihin-family default case (array `ch` in `m.java:204`): decoded record values `0x0100`, `0x0001`, `0x5103`, `0x0139`, `0x0007` (recurring across the 15 polled slots). - **Not yet labeled** — which record is RPM vs. throttle position vs. coolant temp isn't identified from the decompile alone (the field-name mapping lives in UI code not yet fully traced). **Fastest way to finish this isn't more static tracing — it's empirical, once the cable's connected**: poll all 5-ish records in a loop with ignition on, rev the engine / move the throttle by hand, and see which values respond to what. RPM scales with engine speed, TPS jumps with throttle movement, coolant temp changes slowly. Should take one bench session to label all of them. ### What data we actually need, and where each piece comes from | Data | Source | Status | |---|---|---| | RPM | ECU, live poll (`0x21`) | mechanism confirmed, record ID unlabeled | | Throttle position (TPS) | ECU, live poll (`0x21`) | mechanism confirmed, record ID unlabeled | | Coolant temp (exclude cold-engine data) | ECU, live poll (`0x21`) | mechanism confirmed, record ID unlabeled | | Wideband AFR | **external hardware, no way around it** | ECU's own O2 is narrowband, useless for rich-mixture tuning regardless of any of the above | Everything except the wideband AFR reading can come from the ECU itself, via hardware already owned (the K-line/FTDI cable this project needs anyway for `tunie info`) and code already 90% built (`tunie`'s existing transport + safety layer, just needs a polling loop added instead of a one-shot sweep, plus the 2-byte-record variant of `READ_DATA_BY_LOCAL_ID`). **No new ECU-side hardware, no dependency on TuneECU's app, no uncertainty about a proprietary analog-input cable** — that whole question from the previous plan is moot. ### The "onboard live processing unit" idea — sound, and better than the alternatives A small onboard computer (Pi Zero, ESP32-class board, etc.) running two things in parallel, writing one synced timestamped log: 1. `tunie`'s KWP2000 poller hitting the labeled RPM/TPS/coolant records in a tight loop over the K-line cable — safe by construction, same read-only transport already verified against this bike's protocol. 2. An ADC channel reading the wideband controller's analog AFR output. This **eliminates the correlation problem entirely** — one log, one timestamp column, no post-ride guesswork matching a wideband recording against hand-marked throttle positions. It also removes the riding-skill burden from the original plan (no need to hit exact throttle marks by feel — real TPS is captured directly, at whatever resolution the polling loop achieves). **Still required, unavoidably:** the wideband sensor + bung + controller (external AFR hardware, see below) and someone actually riding the bike through the RPM/throttle range while the logger runs — the bike still has to physically experience the conditions being tuned for. What this approach removes is the *ambiguity* in capturing that, not the ride itself. **Concrete next steps, in order:** 1. Extend `tunie` with a 2-byte-record variant of `READ_DATA_BY_LOCAL_ID` and a polling-loop mode (currently only does the one-shot `identify` sweep). Software-only, doable now, no bike required. 2. First ECU contact (`tunie info`, per `../STATUS.md` — still blocked on the connector cable) doubles as the session to empirically label which record is which by watching values change. 3. Once labeled, wire in the wideband ADC channel alongside the KWP2000 poller on the same onboard unit. 4. Do the actual road/bench data capture, post-process off-bike into F Trim corrections exactly as described above — just with a clean single synced log instead of two to hand-correlate. --- ## Wideband O2 + datalogger setup plan (not yet acquired — hardware/shopping list) The German guide's original hardware (Wayne Macdonald's proprietary TuneBoy/TuneEdit cable with a built-in analog input) is **TuneBoy-specific, not confirmed to exist for TuneECU** — this project uses TuneECU (Alain Fontaine's tool), a different product built on the same underlying Keihin/Sagem architecture. Don't assume TuneECU's cable has an equivalent analog input; not verified either way. **Practical path that works regardless:** use a self-contained wideband controller with its own onboard/SD datalogging, rather than depending on feeding the signal into TuneECU's cable directly. Correlate logs to fuel table cells by throttle position + RPM (the German guide's own low-tech method — marked throttle-grip positions, steady-state RPM sweeps) instead of a live electrical link. Candidates, all with standalone logging: - **Innovate LM-2** or **MTX-L** — two programmable analog outputs, widely documented for this exact "log AFR, correlate to ECU table cells" workflow on other platforms. - **AEM X-Series UEGO** — 0-5V analog out + onboard SD logging, CAN option. Both need: a bung in the exhaust for the sensor (weld-in, ideally before any downpipe junction — check whether the Arrow 2-in-1's collector has a provision, or where the stock O2 bung sits since it may be reusable), a controller box (fits under the seat, same area the SAI solenoid used to occupy), switched 12V power, and a way to read RPM alongside AFR (either the wideband unit's own RPM input if it has one, or TuneECU's own live Dashboard screen running in parallel, watched/logged manually). **Setup steps, when the hardware is in hand** (this project can't do this part — physical bung welding, sensor mounting, wiring): 1. Weld/fit the wideband bung, mount the sensor. 2. Mount the controller, wire switched 12V + ground + sensor. 3. Power on, let the sensor heat-soak per its instructions before trusting readings. 4. Load a flat/rich baseline tune (per the German method: AF1/AF2 set to a uniformly rich target, F Trim zeroed) so there's no lean risk during logging. 5. Do the steady-state throttle sweeps, log AFR vs. target. 6. Enter corrections into TuneECU's **F Trim** table (swipe right from the F table screen), **Commit trims**, reflash, repeat 3-4 passes. 7. Set real AF1/AF2 targets once the base fuel table is corrected. This is genuinely the first step in this plan that requires physical hardware acquisition and bike work — everything before it in this document was achievable offline. Flag it to revisit once you're ready to buy parts. ## Stop the SAI solenoid clicking Already physically removed the SAI plumbing; solenoid left wired, clicking harmlessly (confirmed no DTC risk, no engine risk). Fix: swap the solenoid for a **470 Ω / 0.5 W resistor** across the harness connector's two solenoid pins (battery disconnected while wiring). Full procedure and alternate 50 Ω/10 W option: [`COMMUNITY_TUNING.md`](COMMUNITY_TUNING.md#practical-silencing-a-removed-sai-solenoid-with-a-resistor-aug-2026). Then flash `derived/20262-noSAI-only.hex` to suppress the resulting DTC — device flag toggle is confirmed clean/disjoint from the Arrow calibration (#2 above). --- ## After tuning: the wideband hardware isn't permanent Once the F Trim corrections are validated and Committed into the permanent FuelMap, the wideband sensors were a measurement tool, not a runtime requirement — pull them and **plug the bungs with standard M18x1.5 threaded plugs, not a weld**, so they stay reusable for a future re-tuning pass without welding again. Optional, not required: leave one wideband installed permanently as an ongoing AFR gauge — worth it specifically because a fully SAI/O2-deleted bike has zero live feedback watching for a lean condition afterward. Detail: `TUNING_IMPL_PLAN.md` Phase 5. ## Best version yet: reuse the stock O2 bungs, make the O2-delete decision reversible too **Instead of drilling anything, or permanently deciding O2 delete now:** temporarily unscrew the stock narrowband O2 sensors from their existing bungs, thread wideband sensors into those same stock locations for the tuning sessions (M18x1.5 is standard — no adapter expected, though worth confirming on the actual bike), then **put the stock narrowband sensors back afterward**. Zero new holes, zero permanent exhaust modification, and it reuses hardware the bike already has. **This surfaces a real tension worth being explicit about, though:** the original reason for wanting O2 delete wasn't the road-tuning project — it was `RESEARCH.md` §9's finding that O2 delete lets the ECU run a richer, cooler idle/cruise mixture and fixes snatchy off-idle response. That's a **closed-loop** behavior change, at idle/steady-cruise. The wideband/F Trim tuning process only ever corrects the **open-loop** main fuel table (WOT/acceleration) — it doesn't touch idle/cruise closed-loop behavior at all. So if the stock sensors go back in *and* the final flashed tune re-enables the O2 device flag, you get an accurately-tuned WOT table for the new exhaust, but the original snatchy/hot-idle problem O2 delete was meant to fix **comes back** — the two projects don't automatically bundle. **The actual best move: decouple the physical sensor from the software flag.** Put the stock sensors back in the bungs either way (no permanent mod, always reversible) — but the *final* flashed tune's O2 device flag is an independent choice from whether a sensor happens to be physically present: - Flag **off**: ECU ignores whatever's in the bung, runs open-loop-derived richer idle/cruise — gets the original SAI/O2-delete benefit, with a stock sensor sitting there doing nothing (harmless). - Flag **on**: full closed loop restored, snatchy/hot-idle behavior returns, but with the WOT table now correctly matched to the exhaust. Since nothing physical differs between these two states, **switching between them later is just reflashing a different device-flag setting — no hardware change either way, forever.** That's a stronger "no permanent mods" outcome than just avoiding drilled holes: the O2-delete decision itself becomes fully reversible, not just the sensor mounting. **One session-scoped exception:** during the actual tuning rides themselves, closed loop needs to be bypassed regardless of the final plan — a wideband sensor occupying the narrowband's physical spot can't also feed the ECU a valid narrowband signal, so the ECU can't run real closed loop during that window. This matches the original German guide's approach and is normal for any tuning session (dyno tuners do the same) — it's a temporary session state, not a permanent change, and doesn't affect which final flag setting gets chosen afterward. **Practical note:** repeatedly swapping sensors in and out of the same bung across multiple tuning passes is real wear — use anti-seize on the threads to avoid the bung seizing after heat cycles. ## The one recurring trap: ECU generation **Every dead end and wrong turn in this research traced back to the same mistake: mixing mechanical-odometer and LCD-odometer map/ECU generations.** They use different `ecu` type codes in `maps.json`, different absolute byte offsets for the same logical parameter (confirmed: our device-flag offset reads garbage on an LCD-odo file), and are not interchangeable in any way. **Before using any map ID or byte offset found anywhere — forum, vendor site, or this project's own derived files — check it's the mechanical-odo family (`ecu='0'`, or explicitly labeled "Mechanical odometer").** Wrong-generation map IDs that came up and got ruled out: `20500`, `20505`, `20507`, `20516`, `20498...LCD`, `20500MapAirboxBonnie_LCD`. --- ## Local research assets (what's actually on disk) - `reference-maps/*.hex` — real downloaded reference maps: stock production/aftermarket (`20187`-`20192`), Arrow 2-in-1/2-in-2 all fuel variants (`20262`-`20316`), the real SAI/O2 delete map (`20188Map2009AIRBOXBONNY.hex`), the America dyno tune (`20184dynoTuneSteveO2-Disable.hex`) plus its stock baseline (`20185`/`20186`). **Committed to the repo as of 2026-09 (`.gitignore` rule removed, explicit user call)** — no longer working-files-only, reachable from a phone browser for TuneECU use. - `reference-maps/derived/` — our own composed candidates. **Flashable — the checksum problem is solved** (`reference-maps/checksum.py`, see `docs/ROADMAP.md`). `20262-arrow-delete-lowthrottle-composed.hex` has been flashed on the real bike and road-tested successfully — see `ROAD_TEST_LOG.md` for actual outcomes, not just derivation. - `reference-maps/{decode_map,reconstruct_rom,table_map,toggle_devices, compose_arrow_delete,compose_lowthrottle,checksum}.py` — the working toolchain: decrypt → flat ROM → named tables → device-flag edit → trim-byte compose → low-throttle table compose → checksum patch. - `tuneecu_site_mirror/` — full local mirror of tuneecu.net's documentation (built by `tuneecu_crawl.py`), including the German road-tuning primer. The live site says it won't be updated after September 2024 — this mirror is insurance against link rot. - `PERMUTATIONS.md`, `TABLES.md`, `DEVICES.md`, `COMMUNITY_TUNING.md` — the detailed research trail (how the ROM format works); this file is the index. **`ROAD_TEST_LOG.md`** — the separate, real-world log: what actually happened flashing and riding on it, distinct from derived facts about the ROM. ## Open questions, if this gets picked up again 1. ~~Byte→percent scaling formula for the "Idle Fuel Trim (CO)" table~~ — **upgraded, Aug 2026** (`reference-maps/TABLES.md`): found TuneECU's own CO-Trim edit dialog and a live-value dispatcher, both independently confirming the raw byte is **signed two's-complement (-128 to 127), used directly with no scaling factor** — not the unsigned 0-255 interpretation used everywhere earlier in this project. This also retroactively explains away the "255 looks like a sentinel" puzzle from `PERMUTATIONS.md`: unsigned 255 is just signed -1, an ordinary near-neutral value. Encoding confidence: low → medium-high. Still open: the exact real-world unit (percent, or an ECU-internal unit close to it) isn't separately confirmed. Matters for both project goals per the reasoning below, now on firmer ground than when this was first flagged. 2. Map the ~50-byte region (`0x50010`-`0x50C21`) the America dyno tune also rewrote — likely ignition/limiter/other trim, unmapped. 3. Identify `0x53822` — probably traction control or instrument-cluster deactivate per the Android UI's device list, unconfirmed for this ECU family. 4. Actually try the road-tuning method (#5) once the airbox mod happens — the real next step, not more offline byte analysis. 5. First ECU contact (`tunie info`) — still blocked on the physical cable adapter, per `../STATUS.md`. Would resolve which exact stock map is currently flashed, a prerequisite fact this whole document has been assuming rather than confirming.