# Community tuning knowledge — SAI/O2/exhaust, 865 twin External research (Aug 2026), collected while deciding how to fuel-map the 2010 T100 for an Arrow 2-in-1 system. This is **secondhand community/vendor information**, not independently verified against our own reversing — cross- check against `reference-maps/DEVICES.md` and `TABLES.md` before trusting it over what we've actually decoded. ## The standard mod order Community consensus (multiple sources) for the air-cooled 865 twin: 1. O2 sensor removal 2. SAI (secondary air injection) elimination 3. Airbox baffle/snorkel removal 4. Free-flowing exhaust 5. Re-tune to match "Once you've made these mods, you want to tune your engine to take full advantage of them" — each step changes what the engine ingests/exhausts, so tuning last is deliberate, not incidental. [triumphbonneville.org: EFI Tuning – Triumph Twin Power](https://triumphbonneville.org/efi-tuning-triumph-twin-power/) ## IMPORTANT correction to our own assumption: what the TuneECU checkbox actually does Multiple independent forum threads describe the SAI/O2 checkboxes in TuneECU's own Map Edit → Parameters → Devices screen (the same UI our `viewer/` mimics, and the same bytes we found at `0x53801`/`0x53818`/`0x53819`) as **only suppressing the fault-code/CEL logic for a missing sensor — not a functional open-loop switch**: > "This does not disable either system though, only stops the CEL appearing." > "Simply unchecking the box in TuneECU will only prevent warning lights from > appearing — it won't actually disable the hardware itself. The sensor will > still function unless physically removed or blocked off." [triumphrat.net: Desactivate the O2 sensor on TuneECU](https://www.triumphrat.net/threads/desactivate-the-o2-sensor-on-tuneecu.200858/), [thespeedtriple.com: How to turn off O2 sensor on TuneECU](https://www.thespeedtriple.com/threads/how-to-turn-off-o2-sensor-on-tuneecu-to-eliminate-check-engine-light.39850/) **This needs reconciling with `DEVICES.md`**, which currently states flipping these bytes "forces the ECU open-loop." The two claims aren't necessarily contradictory — `DEVICES.md` also notes the real NO-SAI/NO-O2 reference map changed *two more bytes* beyond the three device flags (`0x5369C`, `0x536AB`, described there as "idle/open-loop trim that comes with removing the O2 feedback"). It's plausible the **device-enable bytes alone only gate the DTC logic**, and the *actual* closed-loop-vs-open-loop fueling behavior lives in those separate trim bytes (or elsewhere, e.g. the lambda-target curve at `fe[2]`/`0x52260` noted in `TABLES.md`). Until we isolate that separately, **do not assume our hand-toggled `derived/*-noSAI-noO2.hex` files change fueling behavior** — they very likely only suppress fault codes, matching what these threads describe. Treat them as "silences the CEL for missing hardware," not "retunes for open loop." Real open-loop fueling probably requires touching the lambda-target curve too — worth a dedicated diff pass against a real delete map that isolates just the trim bytes from the device flags. Practical implication carried over from these threads: **if you physically remove the O2 sensors, they must actually be unplugged/removed** — leaving them wired with the checkbox off does not change what the ECU reads from them, per the community description above. ## TuneECU map catalogue IDs seen in the wild (verify before trusting) Forum threads reference these Bonneville map IDs for O2/SAI removal + aftermarket exhaust: | ID | Claimed to be | Checked against our `maps.json` | |---|---|---| | `20516` | "aftermarket exhaust... SAI/O2 removed" | **Wrong bike for us** — actually `ecu='E'`, America/Speedmaster, **LCD odometer**. Not compatible with the 2010 mechanical-odo T100. | | `20500` | "OEM Arrow 2-2, up to E25" | `ecu='8'`, **LCD odometer**. Wrong generation for us. | | `20505` | "OEM Arrow 2-2, up to E10" | `ecu='8'`, **LCD odometer**. Wrong generation for us. | | `20507` | "aftermarket silencers, up to E25" | `ecu='8'`, **LCD odometer**. Wrong generation for us. | None of these are usable for the mechanical-odo 2010 T100 — they're all a later ECU generation (post-2016 water-cooled-era Bonnevilles use LCD odometers and a different `ecu` type code in the catalogue). **Lesson: always cross-check a forum-cited map ID against `maps.json`'s `ecu`/odometer fields before using it** — the ID numbering isn't obviously tied to hardware generation, and it's easy to grab the wrong one. [triumphrat.net: Tune ECU Mapping for O2 and SAI removal - Bonneville](https://www.triumphrat.net/threads/tune-ecu-mapping-for-o2-and-sai-removal-bonneville.755058/) No official TuneECU catalogue entry for the **mechanical-odo** Bonneville (any exhaust, any of the ~163 entries we have) ships with SAI/O2 off — see prior finding, still holds after this search. ## Commercial alternatives to a hand-toggled map Several vendors sell purpose-built, dyno-derived 865-twin tunes rather than a flag flip on a stock map — the safer route per our own `DEVICES.md` recommendation: - **Triumph Twin Power (TTP)**, run by Mike Cripps — "865 EFI Tune Map 3" is built for Stage 1 mods (aftermarket exhaust incl. Arrow, SAI removed, O2 removed), delivered via TuneECU/TuneLoader software or mail-in ECU reprogram. TTP reportedly offers **17 different Bonneville tune variants** for different mod combinations. Currently listed out of stock on their site. [triumphtwinpower.com: 865 EFI tune map 3](https://www.triumphtwinpower.com/bonnevillet100-performance-tune-3.php?review=all), [triumphbonneville.org write-up](https://triumphbonneville.org/efi-tuning-triumph-twin-power/) - **DNK TuneWorks** — $275, mail-in ECU service. Explicitly offers "Enable/Disable Secondary Air Injection" and O2 sensors as a configurable option, plus top-speed-limiter removal, AFR/timing optimization at full throttle, deceleration-pop reduction, and speedometer correction (relevant since our bike's mechanical odo/speedo would need correction if final gearing or wheel/tire size changes). [dnktuneworks.com: Bonneville T100/SE (865)](https://dnktuneworks.com/product/triumph-bonneville-900/) - **British Customs** tuning guide recommends, for 2008–2017 air-cooled Bonneville/T100: the catalogue "Arrow 2-2" maps for slip-ons, "Arrow 2-1" for full systems/performance packages, or a custom tune from DNK/TTP — explicitly warns against self-tuning without dyno validation ("an incorrect tune can create running issues or even cause damage to your bike"). [britishcustoms.com: Triumph Motorcycle Tuning Guide](https://britishcustoms.com/blogs/bc-blog/triumph-motorcycle-tuning-guide) ## Full ROM-level tuning path (beyond TuneECU's catalogue) For editing tables directly rather than picking from TuneECU's preset list: - **OldSkullTuning** and **Tuniverse** sell XDF definition files (~€70) for TunerPro, covering the Keihin **SH7054** (our ECU family) and the related **SH72531**. The XDF exposes fuel tables, spark advance by gear, idle speed, RPM limiter, and **"lambda deactivation"** as editable parameters — the last one likely corresponds to the AFR/lambda curve we already located at flat-ROM `0x52260` (`fe[2]` in `TABLES.md`), which is a stronger lead on where real open-loop behavior is actually controlled versus the device on/off bytes. [oldskulltuning.com: Triumph Keihin SH7054 TunerPro Maps](https://oldskulltuning.com/triumph-keihin-sh7054-tunerpro-maps/) - This path requires a **ROM dump** (Phase 2 in `docs/ROADMAP.md`, not yet implemented — `tunie` is read-only by construction) — XDF+TunerPro edits the raw ROM, not the encrypted download-format `.hex` our `decode_map.py`/ `toggle_devices.py` work with. Two different file formats, same underlying address space. ## Practical procedural notes from the community (unverified, for later) - One installation note (TTP-specific, may not generalize): during whatever post-flash adaptation/reset procedure applies, "pull out the cold start knob only long enough to get your engine started" — leaving it out too long during the reset apparently corrupts the adaptation and forces a restart of the procedure from a cold engine. Relevant once we're actually flashing; irrelevant to read-only work now. - Community sentiment is split on hand-toggling checkboxes on a stock map: several threads describe doing exactly that (download OEM aftermarket- silencer map, untick SAI/O2 in Map Edit, reflash, clear codes) as a known, common shortcut — but per the correction above, that shortcut may only be silencing warning lights, not actually retuning for open loop. Worth weighing against a matched TTP/DNK map before trusting it as a real tune. ## Existing tunes for airbox removal + 2-1 exhaust (Aug 2026) **TTP did/does sell exactly this combination** — but it's paid, and every variant is currently out of stock (consistent with the TTP-closure finding above): | Tune | Mods | Price | Stock | |---|---|---|---| | `865 EFI Tune Map 1 2-1` | 2-1 exhaust + Breathe HiFlo intake **cover** (airbox shell kept) + DNA filter + O2 removal + Stage 1 ignition | £130 (~$177 USD) | Out of stock | | **`865 EFI Tune Map 2 2-1`** | 2-1 exhaust + intake cover + **airbox baffle removed** (not full removal) + DNA filter + O2 removal — **best match to this build**, and the exact combo the Aug 2026 dyno data above (69.02 BHP/54.38 ft-lb) is for | £130 (~$177 USD) | Out of stock | | `865 EFI Tune Map 11 2-1` | **Full airbox removal** + pod filters + 2-1 exhaust + O2/SAI removal (per customer reviews: "removed the O2's and fitted a 2/1 tec pipe", "Air box removed... 2 into 1 with O2 sensors and air injection system removed") | not listed | Out of stock | | `865 EFI Tune Map 13` | Long free-flow silencers (Arrow 2-2 named explicitly, not 2-1) + pod filters + performance cams + O2 removal | £130 (~$177 USD) | Out of stock | [triumphtwinpower.com: 865 EFI tune map 1 2-1](https://www.triumphtwinpower.com/bonnevillet100-performance-tune-1-2-1.php?review=all), [triumphtwinpower.com: 865 EFI tune map 2 2-1](https://www.triumphtwinpower.com/bonnevillet100-performance-tune-2-2-1.php), [triumphtwinpower.com: 865 EFI tune map 11 2-1](https://www.triumphtwinpower.com/bonnevillet100-performance-tune-11-2-1.php?review=all&ro=2), [triumphtwinpower.com: 865 EFI tune map 13](https://www.triumphtwinpower.com/bonnevillet100-performance-tune-13.php) **Site-wide stock-status trap, found Aug 2026:** TTP's master listing page (`triumph-bonneville-t100-865-efi-tunes.php`) shows every single tune as "In stock" — this is wrong, a catalogue-display artifact, not real inventory. Verified directly: `Tune 2 2-1`'s own product page clearly shows "We don't currently have that one in stock." **Always check the individual product page, never trust the listing page's stock column.** **Free, from TuneECU's own catalogue: none.** Dumped and grepped all 163 Bonneville entries in `maps.json` for any of "air[box]", "race", "perform[ance]", "competition", "track", "open" (as in open/velocity-stack airbox) — zero matches, on top of the earlier zero matches for delete/SAI/O2/lambda language. TuneECU never shipped an official Bonneville calibration that assumes the airbox is removed, at any exhaust configuration, free or otherwise. The only two "airbox removed" reference points that exist anywhere are: the paid, currently-unavailable TTP tunes above, and the single community `2009AIRBOXBONNY.hex` file (aftermarket silencers, not Arrow, not in this repo). **Checked catalogue-wide, not just Bonneville:** grepped all 1811 entries in `maps.json` (every Triumph model TuneECU covers) for "airbox"/"air box"/ "velocity stack"/"open filter"/"race filter"/"competition filter" — **zero matches, for any model.** TuneECU has never shipped an official airbox-removed calibration for anything. There's no "independent airbox tune" sitting in the free catalogue to pair with the independent Arrow 2-1 tune (`20262`) — the airbox side of this doesn't exist as a catalogue entry at all, official or otherwise, so there's nothing free to merge in the first place. And even if there were, the overlap finding two sections up means a byte-level merge of two independent deltas isn't sound anyway — this closes off the "merge two catalogue tunes" idea from both directions (no second input map to merge, and the merge mechanism itself doesn't work at the byte level). **Bottom line: no free airbox-removal-matched tune exists for this bike, from TuneECU or anywhere else found so far.** If airbox removal stays in the plan, the realistic options are (a) pay for TTP stock to return, (b) a DNK Tuneworks mail-in tune, or (c) run without airbox removal and keep just Arrow 2-1 + SAI-flag-off, which *is* fully covered by the free catalogue (`derived/20262-noSAI-only.hex`) plus the resistor swap above. ## Can we build "Arrow 2-in-1 + airbox removal + SAI delete" ourselves? Split by what's actually being asked, because the answer is different for each piece: **"Suppress the SAI fault code so the resistor-swapped solenoid doesn't throw a DTC" — yes, solved, already built.** This is exactly what the `0x53801` device-enable byte is for, per the checkbox research above (it's specifically a fault-suppression toggle, not an engine-behavior one) — so using it for this purpose is squarely its intended function, not a misuse. `toggle_devices.py --only-sai` produces this on any base map: ``` derived/20262-noSAI-only.hex (Arrow 2-in-1, t202, E10, SAI flag off) derived/20313-noSAI-only.hex (Arrow 2-in-1, t203, E10, SAI flag off — same tune, see PERMUTATIONS.md) ``` Verified byte-identical to the stock `20262`/`20313` Arrow calibration except the single SAI flag (`1 -> 0`). No effect on the O2 flag, so this doesn't touch anything O2/closed-loop related — narrowly scoped to exactly the resistor-solenoid scenario. This part carries the confidence level `DEVICES.md` already assigns to that offset (~90%, corroborated two independent ways). **"Airbox removal, fuel-matched to Arrow 2-in-1" — no, not with what we have.** `research/reference-maps/PERMUTATIONS.md` (full cross-diff of all 12 official mechanical-odo Bonneville calibrations) found that exhaust-delta and fuel-delta bytes overlap heavily (4149 of 4809 fuel-delta bytes sit inside the exhaust-delta footprint) — different mods' corrections land on the *same* fuel-table cells, so byte-level composition isn't valid; it would overwrite one correction with another rather than combining them. We also don't have an airbox-removal reference for the Arrow-2-in-1 case at all — the one community airbox/SAI/O2-delete file we know of (`2009AIRBOXBONNY.hex`) is for **stock aftermarket silencers, not Arrow**, and isn't currently in this repo to even re-derive a delta from. **Bottom line:** flip the SAI flag on `20262`/`20313` now — that solves the actual near-term problem (no DTC once the resistor goes in) and is low-risk since it's using the flag for exactly what it does. Don't try to DIY the airbox-removal fuel enrichment on top of Arrow 2-in-1 — that's either a real dyno tune (DNK, since TTP's storefront is effectively wound down) or the bigger table-level-arithmetic project noted in `PERMUTATIONS.md`, not something the current byte-diff tooling can do safely. ## Open questions this raises for our own reversing - Isolate whether `0x5369C`/`0x536AB` (or the lambda curve at `0x52260`) are what actually changes fueling behavior, separate from the three device- enable bytes — would resolve the discrepancy between "DEVICES.md says open-loop" and "forums say checkbox is CEL-only." - If we can get a second community NO-SAI/NO-O2 reference `.hex` (ideally Arrow-specific) independent of `20188Map2009AIRBOXBONNY.hex`, diffing three maps instead of two would separate device-flag bytes from fuel/trim bytes more confidently than the current single-diff derivation. ## FOUND: tuneecu.net's own custom-map archive (Aug 2026, supersedes below) The `2009AIRBOXBONNY.hex` we'd been treating as unreachable was never behind a forum paywall — it's sitting in **`tuneecu.net`'s own site**, one click from the page the user asked us to check (`TuneECU_En/mapedit.html` -> nav -> `Links&Downloads` -> `Map_Database.html` -> `Twin_Custom_Tune_list.html`, the community/custom-map equivalent of the official `Twin_OEM_Tune_list.html` catalogue `maps.json` was built from). Downloaded directly, no forum account needed: - `20188Map2009AIRBOXBONNY.hex` — the file `DEVICES.md` was built from. Re-diffed against a fresh `20188Map.hex`: **reproduces exactly** — same 1428-byte/0.36% diff, same three device-flag bytes, same two trim bytes. `DEVICES.md`'s findings are now independently re-verified, not resting on an unreachable prior-session file. - `20500MapAirboxBonnie_LCD.hex` — a second delete map, **Arrow 2-in-2 + airbox removal + no SAI + no O2**, closer to what we actually want than the aftermarket-silencer one. Caveat: **LCD odometer**, wrong ECU generation for the mechanical-odo T100 — and confirmed *not* portable: reading its bytes at the mechanical-odo device-flag offset (`0x53801`) gives garbage (`255`, not `0`/`1`), because the calibration-metadata pointer table (`fe`, `TABLES.md`) is per-map-signature, not a fixed address across ECU generations. Useful as a *second data point on the delta shape*, not as a byte-for-byte reference for our bike. - `904bigvalvegasflowed790cam44mmTBshortExhaust.hex` — a built-engine (big-valve head, cams, 44mm throttle bodies) custom tune. Not relevant to a stock-engine delete/exhaust question, noted for completeness. Both usable files are now in `reference-maps/` (not gitignore-lost this time — the `.hex` gitignore rule is at the samplez repo root, these are committed-adjacent working files, just excluded from git; keep them on disk locally for future sessions). ## What the real device-flag file tells us about composing Arrow + delete With `20188Map2009AIRBOXBONNY.hex` in hand, checked precisely which of its 1428 changed bytes overlap with the Arrow 2-in-1 exhaust delta (`20187`->`20262`, 7548 bytes, from `PERMUTATIONS.md`): - **1344 of 1428 delete-delta bytes (94%) overlap the exhaust delta** — as expected from the earlier finding, both land on the same main VE/fuel table cells. - **The 7 bytes outside the fuel-table regions** are the ones that actually matter, and they split cleanly into two groups: | Bytes | In exhaust delta too? | Composable? | |---|---|---| | `0x53801`, `0x53818`, `0x53819` (SAI, O2×2 device flags) | **No — untouched by the Arrow delta** (both maps hold `1` there) | **Yes, cleanly.** Confirms `toggle_devices.py` applied to `20262`/`20313` is a sound, conflict-free composition — this is exactly what `derived/20262-noSAI-noO2.hex` already does. | | `0x5369C` (104→69 under Arrow, vs. 255→117 under delete), `0x536AB` (125→133 under Arrow, vs. 128→138 under delete) | **Yes — both maps change these two idle/open-loop trim bytes, to different values** | **Yes, both — see correction below.** Initially treated as a conflict because `0x5369C` was read as unsigned; in signed space there's no conflict, both compose additively. | **Update (Aug 2026): both trim bytes compose, not just one.** The "genuine conflict" on `0x5369C` was an artifact of unsigned byte reading — see the full correction further down this document (`TABLES.md` established the table is signed). With correct signed arithmetic, `0x5369C` composes the same way `0x536AB` does, saturating at `+127` rather than needing to be excluded. `compose_arrow_delete.py` now composes both. - The three device flags: safe, already done, zero risk of stepping on the Arrow calibration. - The two idle-trim bytes: both now composed with signed arithmetic. - The remaining fuller table-level VE arithmetic project from `PERMUTATIONS.md`, if the main-table overlap (the 1344 bytes) is worth resolving too, is still open — that's a materially bigger project than these two isolated bytes. - The big 1344-byte overlap in the main VE tables remains the real unsolved part — same conclusion as `PERMUTATIONS.md`: needs 16-bit-cell arithmetic composition, not byte-patching, to combine "richer for Arrow" and "richer for no-O2-feedback" into one number per cell rather than picking one. ## The two conflicting bytes, resolved (Aug 2026) Dug into `0x5369C` and `0x536AB` individually rather than treating them as one problem. Pulled the same two addresses across all four E10/E25 x production/aftermarket stock pairs (`20187`/`20191` production, `20188`/`20192` aftermarket) to see whether each byte behaves like a small linear trim or something else: | Byte | 20187 (prod E10) | 20191 (prod E25) | 20188 (after E10) | 20192 (after E25) | 20262 (Arrow) | delete | |---|---|---|---|---|---|---| | `0x5369C` | 104 | 104 | **255** | **255** | 69 | 117 | | `0x536AB` | 125 | 125 | 128 | 128 | 133 | 138 | **`0x536AB` is a normal trim byte** — smoothly increasing, tightly clustered values regardless of exhaust family or fuel type. Composed it additively: `arrow(133) + [delete(138) - aftermarket_stock(128)] = 143`. **`0x5369C` — correction, Aug 2026: this conclusion was wrong, built on an unsigned reading.** At the time, `255` looked like an exhaust-family sentinel distinct from the ~69-104 held elsewhere, so the byte was left untouched rather than composed. `TABLES.md` later established this whole table is **signed** two's-complement (-128..127), confirmed from two independent code paths in the TuneECU app itself. Redone in signed space, unsigned `255` is simply signed **-1** — an entirely ordinary, near-neutral value. There is no sentinel, no regime conflict, no reason to exclude it. Composed the same way `0x536AB` always was: `stock188=-1`, `delete=+117`, `delta=+118`; `arrow=+69 + 118 = +187`, clamped to the signed max `+127`. The correction genuinely wants this cell pushed to its richest possible value at this RPM point — clamping to `+127` is the right outcome, not a sign the byte doesn't compose. `compose_arrow_delete.py` now composes both bytes; `derived/20262-arrow-delete-composed.hex` regenerated accordingly. Built the result: `research/reference-maps/compose_arrow_delete.py`, output in `derived/20262-arrow-delete-composed.hex` and `derived/20313-arrow-delete-composed.hex` — Arrow 2-in-1 E10, all three device flags off, the one genuinely composable trim byte combined, the one non-composable trim byte deliberately left alone with the reasoning recorded in the script's docstring. **What's still outstanding:** the ~1340-byte overlap in the main VE fuel tables (`PERMUTATIONS.md`) — this session resolved the two *isolated* device-adjacent bytes, not the big fuel-table composition question, which still needs 16-bit-cell arithmetic rather than a two-byte spot fix. ## Minor: FAQ confirms device checkboxes are conditionally available `tuneecu.net/FAQ.html` (German) has a direct question: "Why can't I disable my lambda sensor under Map Edit, or the checkbox won't uncheck?" Answer (old Windows version specific): some options need a double-click to take effect, "but there are also options that are not executable [at all]" — i.e. device toggles can be locked/unavailable depending on model/ECU variant. Consistent with `DEVICES.md`'s note that the device-layout logic (`x.a()`) branches per ECU variant, and with this session's discovery of a device flag at `0x53822` on the America map that may not even exist as a concept on the Bonneville. Also: site-wide notice that tuneecu.net "will no longer be updated after September 2024" — the whole project (not just TTP) is in wind-down/archive mode, worth knowing before relying on it long-term. ## Major find: a documented no-dyno road-tuning method (Aug 2026) Buried in the crawled mirror: `Dr_Feinbeins_kleine_Abstimmfibel/t5_kleine_abstimmfibel.htm` ("Dr. Feinbein's little tuning primer", German, dated 2003, credited to TuneBoy/TuneEdit author Wayne Macdonald's tooling). It's a complete, concrete methodology for correcting the main fuel table **on the road, without a dyno**, using a wideband O2 sensor and a datalogger. Summarized in English (this project's translation, not an official one): **The tables it names** (TuneEdit/TuneBoy terms — same Keihin/Sagem architecture TuneECU works with, though possibly not 1:1 identical naming to TuneECU's own UI): - **FuelMap** — intake air mass in mg, indexed by throttle position (TP) and RPM. This is our `F1-F4` main fuel tables. - **AF1** — target air/fuel ratio at part load, indexed by load and RPM. Example given: 13.0-13.5:1 for good part-throttle drivability. - **AF2** — target AFR at full load. Example: 12.5:1 for max power (richer than stoich 14.7:1, leaner than part-load — the classic power-vs-driveability split). Note "full load" isn't the same as "throttle fully open" — at low RPM, full load is reached well before 100% throttle. - **Fuel%trim table** — a **separate 2D correction table** (author states "15x24" values) overlaid on the FuelMap, used *specifically* for this road-tuning workflow. **This is distinct from the "Idle Fuel Trim (CO)" 1D/32-entry array we identified above** — two different named trim concepts in the same architecture, easy to conflate. Fuel%trim is reset to 0 before each measurement pass and has a **"Commit trims to main tables"** function that permanently bakes the correction into the FuelMap and zeroes the trim table again — i.e. it's explicitly a *working* table, not persistent tune data. **The method, condensed:** 1. Fit a wideband O2 sensor (temporary controller, e.g. under the seat) — either replacing the stock narrowband sensor, or via a bung welded into the exhaust on bikes with no stock sensor. **Disconnect the stock sensor and run a tune with closed-loop trim disabled** for the duration — you're substituting your own live AFR reading for the ECU's. 2. Start from a base tune with **AF1 and AF2 both set flat to 13.0** (rich enough to be measurement-safe, no lean risk) and **Fuel%trim table zeroed**. 3. Mark the throttle grip at positions roughly matching the FuelMap's TP breakpoints. On a quiet straight road, hold each throttle position steady through the RPM range (3rd gear, steady climb from ~1500rpm to redline), 3 measurement passes per throttle position, logging RPM, current TP row + offset to the next row, target AFR, and measured AFR. 4. In a spreadsheet, for each logged point, note where it falls between two Fuel%trim table breakpoints (interpolate by the offset), and enter the measured-vs-target AFR discrepancy into that Fuel%trim cell. 5. **"Commit trims to main tables"** — bakes the correction into the permanent FuelMap, zeroes Fuel%trim again, flash back to the bike, repeat. Author reports 3-4 iterations converges to only small residual corrections. 6. Once the FuelMap itself is corrected, set real AF1/AF2 target curves (not the flat 13.0 placeholder) and do a final commit — that's the finished road tune. 7. **A dyno session afterward is optional, not required** — author notes it should only need fine-tuning via Fuel%trim at that point, "not much should come out of it" if the road-tuning was done properly. **Why this matters for the airbox-removal plan:** this is a real, documented alternative to paying for TTP/DNK — the hardware cost is a wideband O2 sensor + controller + a cheap datalogger/laptop setup, not a tuner's fee. It directly targets the exact problem airbox removal creates (the main FuelMap no longer matches real airflow) using the manufacturer's own table structure, correcting *measured reality* rather than guessing at composed deltas the way this project's own `PERMUTATIONS.md`/ `compose_arrow_delete.py` work has been attempting. Worth serious consideration as the actual path forward once the airbox mod happens, independent of whether a commercial tune is ever obtained. **Caveat resolved — confirmed present in the current Android app.** `TuneECU_En/android.html` describes, in TuneECU's own current UI terms, the identical mechanism the 2003 German guide used: > "From the 'F' or 'I' screen, the corresponding corrections **F Trim** > table can be displayed by swiping towards the right." ... "**F Trim > global**: Use the F Trim table for all 'F' tables." ... "**Commit > trims**: Apply each Trim table to the F & I table and reset the F Trim > tables." ... "**Import table PCIII/V**: Import a table PCIII or V in the > trim tables." Same names (F Trim / I Trim, not "Fuel%trim" — terminology drifted slightly from the 2003 TuneEdit doc but the mechanism is identical), same "commit" semantics (bake trim into the main table, reset trim to zero), and it works for both F (fuel) and I (ignition) tables, on the actively-maintained Android app, not a defunct Windows tool. **The road-tuning method above is directly usable today**, not archaeology — this is the real, current path to a properly airbox-matched fuel table without paying a tuner. Also from `android.html`'s UI walkthrough, confirms the **Devices** menu's full generic list (bike-dependent which ones actually show/work, per the FAQ note above): **SAI, O2 probe, Immobilizer** (example given: Ducati Monster), **Traction control, instrument-cluster deactivation** (example: Daytona 675 — lets the engine start without the dash connected). This is a strong candidate identity for the unexplained `0x53822` flag found on the America dyno tune last session — likely traction control or instrument-cluster-deactivate on that model, not something that exists as a concept on the Bonneville at all. Not confirmed, but a much better lead than "unknown." ## Major find: a real dyno tune on our exact ECU generation, airbox removed (Aug 2026) The full `Twin_Custom_Tune_list.html` listing (8 Bonneville/Thruxton/America entries total, not just the 3 found via keyword search) contains one entry much closer to what we actually want than `2009AIRBOXBONNY.hex`: **`20184dynoTuneSteveO2-Disable.hex`** — America/Speedmaster, **mechanical odometer**, base map `20184` (same `t201` mechanical-odo family as our Bonneville, `ecu='0'`). Description: "Open pipe aftermarket K&N air filter short slash cut foran pipe's and the air box **intake snorkel removed**, **O2-probes disabled**, tune read from 08 America, mechanical odometer." A real, named tuner's ("Steve") dyno-derived tune, not a hand-toggled stock map — airbox snorkel removed + open pipes + O2 disabled, on our exact ECU family. [tuneecu.net: Twin_Custom_Tune_list.html](https://tuneecu.net/Twin_Custom_Tune_list.html) Base map `20184` itself isn't live on tuneecu.fr any more; diffed against the closest available stock baseline, `20186` (America, aftermarket silencers, mechanical odo — same family, later revision, same relationship as `20187`->`20191` for Bonneville). **Confirms device flags a third time, independently:** `SAI=1` (left ON — this tuner chose not to delete SAI, only O2), `O2 sensor 1/2 = 0` (both off). Same offsets, third different base map, third confirmation beyond `DEVICES.md`'s original 90%/80%. **Revises the "two conflicting trim bytes" model from earlier this session — it undersold the complexity.** This more aggressive tune (open pipes, not just aftermarket mufflers) doesn't touch just `0x5369C`/`0x536AB` in isolation — it rewrites **nearly every byte from `0x53690` to `0x536AF`** (a ~32-byte span). That means `0x5369C`/`0x536AB` aren't standalone scalar trims; they're two cells inside a **larger fuel-trim table** (glossary confirms TuneECU's own concept of "Idle fuel trim" and "Off idle fuel trim" as dedicated percentage tables, distinct from the main F/L tables — this is almost certainly one of those). The Bonneville-family comparison only touched 2 of this table's ~32 cells because that delta was conservative (aftermarket mufflers only); this dyno tune's more aggressive mods (open pipes) moved most of the table. **Also found a new device flag** at `0x53822` (index 33 past `0x53801`, i.e. one slot past the two O2 sensors) that this tune also disabled — purpose not yet identified; may not even apply to the Bonneville's specific device list (America/Speedmaster could have a different `Devices` array length/order — `x.a()` branch is per-ECU-variant per `DEVICES.md`). **And a whole new region, `0x50010`-`0x50C21`, extensively rewritten** — not yet mapped to any named parameter. Glossary terms that likely live here given what changed: ignition timing, rev limiter, injector pulse time/short-term fuel trim. Not analyzed further this session. **What this means for "have the tune ready for airbox removal":** real progress, but the earlier two-byte fix (`compose_arrow_delete.py`) is now known to be an incomplete model, not a finished one — it correctly handles the 3 device flags and gets lucky on `0x536AB` being a small/isolated correction for the *Bonneville* delta specifically, but a real airbox-removal delta (per this more representative reference) touches a 32-byte trim table plus a further ~50-byte region neither previous composition attempt accounted for. **Don't treat `derived/20262-arrow-delete-composed.hex` as airbox-ready** — it isn't; it was scoped to the SAI/O2 solenoid-removal problem specifically, not airbox removal. Mapping the `0x53690-0x536AF` trim table's axis (RPM? gear? load bin?) and the `0x50010-0x50C21` region is the actual next step before a real airbox-removal composition is possible, and is a bigger project than what's been done so far — worth scoping separately when picked back up. ## Second reference map search (Aug 2026): superseded by the find above, kept for history Searched map-sharing threads, direct filename variants, and vendor sites for a second independent NO-SAI/NO-O2 `.hex` to cross-diff against `20188Map2009AIRBOXBONNY.hex`. Found nothing. One forum result was explicit: > "There's just one map available for TuneECU for 2009 bikes or 2010 with the > mechanical speedo... called [20188Map]2009AIRBOXBONNY.hex" That file appears to be the **only** SAI/O2-delete map that ever circulated for the mechanical-odo generation — originally derived from a British Customs PowerCommander III map (no airbox, K&N pods, British Customs mufflers). Everything else that surfaces (`20500`/`20505`/`20507`/`20516`, `20498...LCD`) is confirmed LCD-odometer, wrong ECU generation. **Caveat on our own `DEVICES.md` findings:** we don't currently have `2009AIRBOXBONNY.hex` sitting in the repo (`.hex` is gitignored, and it wasn't re-fetched this session) — `DEVICES.md`'s SAI/O2 offsets rest on a prior analysis of a file we'd need to re-download to double-check. All its known links are forum attachments behind the same `tollbit.*` paywall blocking automated fetches here; getting it requires a forum login. ## Resolved: what physically removing O2 sensors actually changes Closed-loop (O2-corrected) operation only runs in a narrow band: **idle and steady low-to-mid throttle** (roughly <40% throttle, constant speed, warmed up). **Acceleration and wide-open throttle are already open-loop on the stock map** — the O2 sensor has zero influence there, stock or modified. [motofomo.com: Open Loop vs Closed Loop](https://motofomo.com/open-loop-vs-closed-loop-fuel-injection/), [racext.com: Understanding Fuel Injection and Tuning](https://racext.com/understanding-fuel-injection-and-tuning-open-vs-closed-loop/) So **physically** removing/unplugging the sensors is a real behavioral change (forces open-loop everywhere, richer/more consistent idle-cruise fueling, cools the air-cooled top end, reduces snatchy off-idle response per `RESEARCH.md` §9) — genuinely different from the checkbox-only CEL suppression documented above. But two things temper "O2 delete = more power": 1. **It only affects idle/cruise/part-throttle**, since WOT was already open-loop. Peak-power gain from the O2 delete *by itself* is close to zero — the actual power comes from the exhaust/airbox flow changes, and O2 delete is what lets a richer fuel table for those changes stick without the ECU fighting it. 2. **Unplugging without a matching richer map is a net negative**, not neutral — you lose the live correction that was keeping idle/cruise from running lean, with nothing filling in for it. Several sources describe the ECU's adaptive/long-term trim quietly "learning" a custom map back toward stock stoichiometric if the O2 sensor stays connected and active, which is the flip-side risk (custom tune silently undone rather than running lean). [drdyno.com: O2 sensors and closed-loop systems](http://drdyno.com/AIM_2010-07.html) Conclusion: SAI/O2 delete is a prerequisite that lets a richer tune hold, not a horsepower mod on its own. The `derived/*-noSAI-noO2.hex` files in this repo very likely only silence fault codes (per the checkbox finding above); even a fully "correct" flag-and-fuel-table delete mainly buys cleaner idle/cruise behavior, not peak power. ## SAI removal: actual risk profile (Aug 2026) For someone who's already pulled the physical SAI plumbing (valves/hoses) but left the solenoid wired up: - **No engine-damage risk.** SAI only injects fresh air into the exhaust port, downstream of combustion — it doesn't touch fueling, lubrication, or cooling. There's nothing about removing it that can hurt the engine mechanically. - **The clicking solenoid is normal and harmless.** With plumbing removed but the solenoid still electrically connected, "it will keep clicking merrily away without throwing any ECU codes" — the ECU is still commanding it on its usual duty cycle; there's just nothing downstream for it to move. To actually silence it: replace the solenoid with a ~50 Ω resistor (mimics the coil's electrical load so the ECU sees a normal circuit and stops trying to drive it), or just leave the physical solenoid connected even if unplumbed — either satisfies the ECU without the noise. Simply unplugging the solenoid's connector, by contrast, opens the circuit and *will* trigger a DTC — the software SAI-disable flag exists specifically for that case, again consistent with the earlier CEL-suppression finding. [triumphrat.net: Air injection removal](https://www.triumphrat.net/threads/air-injection-removal.846474/) - **Deceleration popping cuts both ways depending on exhaust.** SAI air helps combust unburned fuel present in the exhaust under deceleration. On an aftermarket pipe (different backpressure/scavenging than stock), SAI presence is reported as a *cause* of decel popping, not a cure — deleting it is the fix, matching this project's own `RESEARCH.md` §9 guidance to disable SAI before tuning for an aftermarket system. [triumphrat.net: Bonneville Exhaust Popping Troubleshooting](https://www.triumphrat.net/threads/bonneville-exhaust-popping-troubleshooting-help.85412/) - **Real risk is regulatory, not mechanical.** SAI is emissions hardware; removing it is a tamper concern only where emissions testing/inspection applies to the bike (varies by province/state; many jurisdictions exempt older motorcycles). Not an engine-safety question. - **One genuine fueling interaction, now moot for this bike:** if the O2 sensor sits downstream of the SAI injection point *and* SAI is still actively pumping air (not just electrically connected with the plumbing removed), the extra O2 in the exhaust can bias closed-loop trim toward reading falsely lean, causing the ECU to over-fuel to compensate — this is the exact mechanism behind `RESEARCH.md` §9's "SAI corrupts AFR readings on a dyno." With the plumbing physically removed (no air actually being injected any more, solenoid clicking into nothing), this interaction is already eliminated regardless of what the software flag says. ## Practical: silencing a removed SAI solenoid with a resistor (Aug 2026) For anyone who's already pulled the SAI plumbing (valves/hoses) and just wants the leftover solenoid clicking to stop, without touching the ECU: The ECU doesn't know or care whether it's driving a real solenoid coil or a plain resistor — it only checks that the circuit shows plausible resistance/current when it commands the output. Two resistor values show up in the community as working substitutes for the solenoid itself: | Resistor | Wattage | Notes | |---|---|---| | 50 Ω | **10 W** (must be this high, or it overheats) | Closer match to the solenoid coil's actual current draw | | 470 Ω | 0.5 W | Simpler/smaller part, most commonly recommended, plenty of margin | [triumphrat.net: Air injection solenoid replacement resistor needed](https://www.triumphrat.net/threads/air-injection-solenoid-replacement-resistor-needed.629554/) Procedure: disconnect the battery negative terminal first (working on ECU-monitored wiring), unplug the solenoid from the harness connector (leave the harness-side connector), wire the resistor across the two harness-side pins that used to feed the coil (push-fit into the connector pins for a quick fix, or crimp/solder + heat-shrink for something weatherproof/permanent), reconnect the battery, clear any DTC. This is purely an electrical placebo for the ECU's continuity check — no functional link to fueling, and separate from the SAI *software* enable flag (`0x53801`), which per the earlier finding is about warning-light suppression, not actuator behavior. A bare disconnected plug (no resistor) *will* throw an open-circuit DTC; a resistor avoids that without needing the software flag at all. The two are complementary, not substitutes — see next section for why you likely still want the flag too. ## Dyno data: exhaust/intake stages, quantified (TTP, 865 EFI twin) Triumph Twin Power publishes an actual dyno comparison for this engine family that answers "how much does each stage actually gain": | Stage | Mods | Peak power | Peak torque | Notes | |---|---|---|---|---| | Stock | SAI removed only | 60.64 BHP | 46.84 ft-lb | torque dip 3500–5500 rpm | | Stage 1 (Tune 1) | Venturi/performance intake **cover** (airbox *not* fully removed) + TORS exhaust + matching TTP tune | 64.06 BHP (**+5.6%**) | 50.78 ft-lb (**+8.4%**) | max torque +15.9% @ 4200 rpm; balanced midrange | | Stage 1.5 (Tune 2) | Stage 1 + **airbox internal baffle removed** (shell kept) + TORS + matching tune | 63.40 BHP (+4.5%) | 51.91 ft-lb (**+10.8%**) | max torque +16.8% @ 4200 rpm | | **Stage 1.5 + 2-1 (Tune 2, 2-1)** | Stage 1.5 (baffle removed, airbox shell kept) + **2-1 exhaust system** + matching tune | **69.02 BHP (+13.8%)** | **54.38 ft-lb (+16.0%)** | **max torque +26.6% @ 4100 rpm** — closest published data point to a bare Arrow-2-1 + baffle-removal build | | Stage 2 (Tune 12) | **Full airbox removal** + pod filters + short free-flow silencers + matching TTP tune | 69.31 BHP (**+14.3%**) | 54.14 ft-lb (**+15.6%**) | max torque +21.0% @ 4300 rpm; more top-end, less mid vs Stage 1.5+2-1 | [triumphtwinpower.com: EFI Model Comparison](https://www.triumphtwinpower.com/triumph-twin-power-torque-model-comparison.php), [triumphtwinpower.com: Breathe Evolution](https://www.triumphtwinpower.com/triumph-twin-power-breathe-evolution.php), [triumphtwinpower.com: Bonneville/Thruxton airbox & exhaust tuning](https://www.triumphtwinpower.com/triumph-bonneville-thruxton-airbox-exhaust-tuning.php) (Aug 2026 addition — the Stage 1.5+2-1 row, closest published match to a 2-in-1 exhaust + baffle-only airbox mod, found via `triumph-twin-power-x-files.php`) Key takeaway: **exhaust + intake cover + matched tune alone is real but modest** (~5–8%); **baffle removal alone adds more torque than power** (Stage 1.5's torque gain nearly matches full removal, its power gain doesn't); **pairing baffle removal with a 2-1 exhaust gets within a rounding error of full airbox removal** (69.02 vs 69.31 BHP, 54.38 vs 54.14 ft-lb) — i.e. per this data, **you may not need to go all the way to full pod-filter airbox removal once a 2-1 exhaust is already fitted; the lighter baffle-only mod gets nearly all of the same gain.** In all cases the number that matters is "matched tune," not the hardware alone — TTP's own comparison doesn't show standalone gains without their tune paired to each stage. Their "Breathe Evolution" intake cover (a snorkel/cover swap, not full removal) claims to track full pod-filter airbox removal almost exactly up to ~6000 rpm, with full removal only pulling ahead above that — i.e. most of the "lose the airbox" gain is available without fully deleting it, up until you're spending real time above 6k rpm. **Airbox internal baffle removal — mechanical procedure**, per TTP's own guide: 1–1.5 hours, requires removing seat, rear mudguard, side covers, air intake, battery, and fuse box to access and slide out the baffle from the airbox's right side. Tools: 3/4/5mm Allen keys, T30 Torx, large flat/cross screwdrivers, 8/10mm sockets/spanners. **Warning specific to carbureted bikes** (not this EFI bike, but worth knowing if cross-referencing): a brittle sensor clip breaks easily during removal on carb models. The guide itself doesn't address any fuel-map/ECU implications of the baffle removal — purely a mechanical how-to, tuning is treated as a separate step (their own tune, in TTP's case).