Files
samplez/tunie/research/TUNING_GUIDE.md
uhryniuk f830f8937d Log real-world road test results and correct/extend the CO-trim theory
New research/ROAD_TEST_LOG.md: the 2026-09-15 flash of
20262-arrow-delete-lowthrottle-composed.hex was a major real-world
success (stalling fully resolved, decel backfire much reduced but not
eliminated). Captures the live diagnostic reasoning that followed:
correcting an overly-lean-leaning framing mid-session (residual decel pop
is more likely a rich-unburned-mixture-with-no-SAI-assist problem, not a
lean one), a structural hypothesis that the Idle Fuel Trim (CO) curve
likely applies whenever the throttle is closed at any RPM rather than
only at a literal stop (only 2 of its 32 points are currently corrected,
and the untouched middle range lines up with the residual pop's RPM
window), and the reasoning for rejecting airbox-baffle removal as a fix
for decel popping specifically (wrong mechanism -- closed throttle isn't
airbox-limited -- and risks worsening the separately-flagged, still
unaddressed main-VE-table leanness gap).

Also updates TABLES.md with the CO-trim applicability open question, and
brings TUNING_GUIDE.md's "local research assets" section up to date (it
still said maps were gitignored/unflashable, both stale).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FP2GaxS9HkUdL5sLBnjKje
2026-09-16 09:07:35 -05:00

31 KiB
Raw Blame History

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). 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, 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 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.

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) 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).

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) — 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. 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.

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.

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 <hi> <lo>. 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. 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.