Repository navigation
Releases
Beta v0.4.3
🚀 sh705x-emu Beta v0.4.3 — compare two flashes, change a map and watch the firmware use it; GDS2 sees live data
The map browser learns what calibration editors do: compare this flash with another file,
copy or save a map, and change a cell. The change goes into the emulated ECU, and its next
lookups use it. The J2534 DLL now delivers the frames GDS2's Data Display streams on.
Context: four changes on top of 0.4.2, checked against the Astra J's own flash
(A17DTJ, Denso, SH7059, NCLSW420AX36), and with GDS2 in a virtual machine.Deliverables:
sh705x-v0.4.3-beta-windows-x86_64.zip:sh705x-gui.exeandsh705x.exe, which
carry their own C runtime.app-arm64-v8a-release.apkandapp-x86_64-release.apk: versionCode 9, versionName
0.4.3. Signed with the same key as 0.3.x and 0.4.x, so they install over them.sh705x_j2534_x86.dllandsh705x_j2534_x64.dllwithregister.ps1.No firmware comes with any of them.
1. Comparing with another flash
- "Compare with a flash…" reads a second image from your disk. Each map is read from
the same descriptor in both files; maps whose cells live in RAM, or whose shape differs
in the other file, are marked as not comparable. - The list marks each map that differs with its changed-cell count (
Δ100), and the
filterDIFFkeeps only those. - "Differences" shows each cell as
this → other, coloured by the size of the change,
with the count and the largest change above. - The other file is kept only in memory; "Stop comparing" drops it.
2. Copying and saving a map
- "Copy" puts the map on the clipboard as tab-separated text, axes included, ready for
a spreadsheet. "Save CSV…" writes the same with commas.
3. Changing a map in the emulated ECU
- In "Table", click a cell, type the value in physical units and press Set (or
Enter). It is encoded in the map's own format: float, or 8/16 bits with the descriptor's
scale and offset. A value out of range is refused, and the limits are shown. - It is written to the emulated flash and, when the firmware reads that map from a RAM
copy, to that copy too. The maps shown and the lookup checks are re-read, so no false
"mismatch" appears. - Changed cells are underlined. "Revert this map" puts back the loaded flash's bytes.
"Save flash as…" writes the image with the changes. Its checksum is not recomputed:
it is for the emulator and for study, not for an ECU. - Checked end to end on the Astra's flash: with the boost target (
0x9DADC) raised
10 % while idling, each later lookup returns exactly the changed map's interpolation
(testa_changed_map_is_what_the_firmware_reads, which runs whenSH705X_FLASHis set). - Only the emulator's memory changes. Nothing is sent to a car.
4. J2534: GDS2's Data Display streams live
- GDS2 opens an ISO 15765 channel with a flow-control filter for the ECU's replies and a
pass filter for0x5E8, where the ECU streams the periodic data it was asked for.
Those frames are single CAN frames outside any flow-control pair, and the DLL dropped
them, so GDS2 reported lost communication. Raw frames that only a pass filter claims are
now delivered. GDS2's Data Display then shows the emulated engine's parameters live, at
1000 rpm as set in the emulator.
5. Smaller fixes
- A map the whole-ROM scan finds no master call for, but which the firmware does look up,
is no longer called "unused" nor dimmed in the list. The scan misses it because the
firmware reaches it another way. - Reading a map from a descriptor that points near the end of the address space no longer
overflows in debug builds.
6. Upgrading
- Windows: unzip over the old folder.
- Android: install over 0.4.2; same key, same data. The in-app updater offers it. The
map changes in §1–§3 are in the Windows window only this time. - J2534: replace the DLLs and run
register.ps1again as administrator. The DLL file
names did not change.
Checked against an Opel Astra J 1.7 CDTI (A17DTJ) in the emulator and with GDS2. Windows
programs built by the release workflow from the tagged commit, checked to import no Visual
C++ runtime. Release APKs built from the tagged commit and signed with the same key as
0.3.x and 0.4.x (certificate SHA-256 5ff00c0a…f515c2).
Beta v0.4.2
🚀 sh705x-emu Beta v0.4.2 — the replay drive, corrected; a whole-ROM map scan; and a J2534 door
A fix to replay so the turbo vanes follow the car instead of their stop; a scan of the
whole ROM for every map and curve; and a J2534 pass-thru DLL so GDS2 and other Windows
diagnostic tools talk to the emulated ECU as if through a real adapter.
Context: five changes on top of 0.4.1, checked against the Astra J's own flash
(A17DTJ, Denso, SH7059, NCLSW420AX36) and a drive recorded from the car.Deliverables:
sh705x-v0.4.2-beta-windows-x86_64.zip:sh705x-gui.exeandsh705x.exe, which
carry their own C runtime.app-arm64-v8a-release.apkandapp-x86_64-release.apk: versionCode 8, versionName
0.4.2. Signed with the same key as 0.3.x, 0.4.0 and 0.4.1, so they install over them.sh705x_j2534_x86.dllandsh705x_j2534_x64.dllwithregister.ps1: the J2534
pass-thru DLL for 32-bit tools (GDS2) and 64-bit ones.No firmware comes with any of them.
1. replay drives the turbo right
- The plant builds manifold pressure as resting pressure + the turbocharger's boost.
replaywas feeding it the recorded manifold pressure as the resting value, so the
boost was counted twice: the firmware saw too much pressure, opened the vanes to their
stop, and the VGT command sat near 11 % against the car's ~58 %. - It now feeds the barometer as the resting pressure, which is what "no boost" means.
The vanes follow the car: the VGT median error drops from 30.2 % to 4.3 %.
2. Comparing two images, and the per-cell table
--compare <other.bin>replays the same route through a second flash and lists only
the cells where the two images compute a different setpoint — rpm, pedal, setpoint,
each image's value and the difference — or says they agree everywhere.--out <file.csv>writes the recorded and computed setpoint of every cell.- Two more setpoints are read when the route logs them: desired EGR (
0x002C) and the
EGR command (0x32A8). tools/route_cells.pynow groups the samples by polling round (a round ends when an
identifier repeats or more than--round-mshas passed), so each cell is a real
operating point rather than a stride of rows. Setpoint columns the route never logged
are left empty.
3. catalog: every map in the ROM, read or not
sh705x catalog(and the map browser in the GUI) scans the whole ROM for map and
curve descriptors and lists them — axes, size, address — whether or not the firmware
read them during the run. It finds the maps the live view cannot, because nothing has
touched them yet.
4. j2534: GDS2 and other tools reach the emulated ECU
- A J2534 (04.04) pass-thru DLL whose "adapter" is the emulator's SM3 (
sh705x sm3) over
TCP. Registered withregister.ps1, J2534 applications on the machine — or in a virtual
machine that reaches it over the network — list it beside real adapters and talk CAN and
ISO 15765 to the emulated ECU. The vendor's own DLL cannot: it pairs with the physical
adapter first and takes no address. - The DLL finds the SM3 at
SH705X_SM3, orserver=host:portin
%ProgramData%\sh705x\j2534.ini, or127.0.0.1:777.
5. The SM3 opening handshake
sh705x sm3now answers the opening exchange with the same lengths the car's Scanmatik
gives, so tools that check them accept the emulated adapter.
6. The in-app updater lists the installers
- The Android updater checked for "install unknown apps" first and, without it, sent you to
a settings screen — which some phones lock, dead-ending the update. It now offers the
installer list first: Install With Options (through Shizuku) installs on its own
authority and needs no such permission. The system installer is asked for only when it is
the only option.
7. What was measured
On the Astra's flash, the Coruña–Santiago–Cangas route, 87 cells (558 494 samples):
| setpoint | median |error| | p90 |error| | constant-guess median |
|---|---|---|---|
| desired rail pressure | 3.03 MPa | 53.4 MPa | 28.05 MPa |
| desired air mass | 1.42 g/s | 8.30 g/s | 32.70 g/s |
| VGT | 4.32 % | 10.97 % | 9.02 % |
| desired boost | 17.50 kPa | 80.0 kPa | 57.69 kPa |
| torque | 35.0 N·m | 134.5 N·m | 102.25 N·m |
Rail pressure, air mass and now VGT land well inside a constant guess: the firmware
explains them. Boost and torque depend on the fuel injected, which is 0 in the emulator
without injection, so they do not match yet.
8. Upgrading
- Windows: unzip over the old folder.
- Android: install over 0.4.1; same key, same data. Install this one by hand once:
phones on 0.4.1 or earlier carry the old in-app updater (§6), which cannot install it on
a phone that locks "install unknown apps". Open the APK with Install With Options this
time; from 0.4.2 on, the in-app updater works. - J2534: place the two DLLs where you like and run
register.ps1as administrator
(see its header for the-Dll32/-Dll64/-Serverarguments).
Checked against an Opel Astra J 1.7 CDTI (A17DTJ) in the emulator. Windows programs built
by the release workflow from the tagged commit, checked to import no Visual C++ runtime.
Release APKs built from the tagged commit and signed with the same key as 0.3.x, 0.4.0 and
0.4.1 (certificate SHA-256 5ff00c0a…f515c2).
Beta v0.4.1
🚀 sh705x-emu Beta v0.4.1 — a recorded drive against the firmware, and standard names
A replay command that drives the firmware through every operating point of a recorded
route and says how far each of its setpoints lands from what the car asked for; the SAE
name for every standard OBD-II PID; and maps grids that say at a glance what they hold.
Context: three additions on top of 0.4.0, checked against the Astra J's own flash
(A17DTJ, Denso, SH7059, NCLSW420AX36) and a 224 000-sample drive recorded from the car.Deliverables:
sh705x-v0.4.1-beta-windows-x86_64.zip:sh705x-gui.exeandsh705x.exe, which
carry their own C runtime.app-arm64-v8a-release.apkandapp-x86_64-release.apk: versionCode 7, versionName
0.4.1. Signed with the same key as 0.3.x and 0.4.0, so they install over them.No firmware comes with any of them.
1. sh705x replay
python tools/route_cells.py <route.csv.gz> --out cells.csv
sh705x replay --flash <flash.bin> --eeprom <eeprom.bin> --cells cells.csvtools/route_cells.py(standard library only) bins a recorded route by engine speed
and pedal travel and keeps the median of each input and setpoint per cell. It turns the
pedal's sensor 1 into travel with the ECU's own calibration (19.6 % at rest, 70 % at full).replaysets each cell's pedal, coolant, intake air, boost, barometric pressure and
air mass, lets the firmware settle, and compares five setpoints with the recorded ones:
desired rail pressure, desired boost, VGT, desired air mass and torque. The cells run in
parallel, asmapsdoes.
2. Standard PID names
- A parameter profile is a generic list and can hang another module's label on a low
identifier. For0x00–0xFFthe emulator now uses the fixed SAE J1979 name instead
(0x000C→ "Engine speed",0x000B→ "Intake manifold absolute pressure"). - Higher identifiers keep the profile's name. It shows wherever variables are named: the
maps window on Windows and the maps tab on Android.
3. maps says what a grid holds
- Under each setpoint: empty, all zero, constant at a value, or the range it spans. "The
same at every point" now carries the value, so a grid of zeros reads as such.
4. What was measured
On the Astra's flash, a route of 59 cells (223 979 samples, Coruña–Santiago–Cangas):
| setpoint | median |error| | p90 |error| |
|---|---|---|
| desired rail pressure | 1.89 MPa | 32.3 MPa |
| desired air mass | 2.08 g/s | 8.30 g/s |
| desired boost | 15.6 kPa | 56.9 kPa |
| VGT | 30.2 % | 48.6 % |
| torque | 27.0 N·m | 80.0 N·m |
Rail pressure and air mass follow the car. Boost, VGT and torque depend on the fuel
injected, which is 0 in the emulator without injection, so they do not match yet.
5. Upgrading
- Windows: unzip over the old folder.
- Android: install over 0.4.0 (or let the app's update detector find it); same key,
same data.
Checked against an Opel Astra J 1.7 CDTI (A17DTJ) in the emulator. Windows programs built
by the release workflow from the tagged commit, checked to import no Visual C++ runtime.
Release APKs built from the tagged commit and signed with the same key as 0.3.x and 0.4.0
(certificate SHA-256 5ff00c0a…f515c2).
Beta v0.4.0
🚀 sh705x-emu Beta v0.4.0 — plugins beside the ECU, and an app that updates itself
WebAssembly plugins that ride the ECU's CAN bus, read the memory they declare and write
RAM, each in a sandbox with a budget per call — installed from a file or from your own
repository, public or private, exactly as opendash does. And an Android app that finds its
own new releases on GitHub and installs them.
Context: the plugin host and the update detector on top of 0.3.2, checked against the
Astra J's own flash (A17DTJ, Denso, SH7059, NCLSW420AX36) and on an Android emulator.Why plugins: the emulator's CAN bus already lets other nodes answer the ECU
(ADR-0005). A plugin is that node, made
installable: it hears the frames the ECU sends and puts frames back on the bus, so you
can stand in for a module the car talks to without touching the emulator's own code
(ADR-0008). Anything specific to a car stays
in a private plugin repository, out of this one.Deliverables:
sh705x-v0.4.0-beta-windows-x86_64.zip:sh705x-gui.exeandsh705x.exe, which
carry their own C runtime.app-arm64-v8a-release.apkandapp-x86_64-release.apk: versionCode 6, versionName
0.4.0. Signed with the same key as 0.3.x, so they install over it.No firmware comes with any of them.
1. The plugins
- Format: a
.wasmcarrying a JSON manifest in itssh705x-pluginsection. It runs on
wasmi, with 20 million units of fuel per call, so a plugin that loops forever is stopped
and the rest of the machine goes on. - API 1:
send,read,write,logandnow_us;init,on_frame(every frame the ECU sends) andtick(everytick_us).
- Sandbox: a plugin reads only the memory its manifest names (up to 64 KiB) and writes
only RAM (FFFE_8000..FFFF_C000); any write outside is refused and logged. - Sources: install from a file, or from GitHub, GitLab, Gitea or a plain HTTPS index —
with a per-source token for private repositories, the same URLs and headers as opendash. - SDK and example:
plugins/sdk(thesh705x-plugincrate, amanifest!macro and
read/write/log/sendhelpers) andplugins/abs-node, which stands in for the ABS:
it sends0x1E9every 21.4 ms, and with it the Astra stops reporting U0121 (no ABS).
2. On each platform
- Windows: a Plugins window — load, enable, see each plugin's log and errors, add a
source and install from it. - Android: a Plugins tab (the 5th), the same, plus loading the enabled plugins at
power-on. Needs the network permission for remote sources.
3. The app updates itself
- Ported from opendash: at start-up (you can turn it off) and by hand, under a new
Updates section, it asks GitHub for the newest release, compares versions by number,
downloads the APK for the phone's ABI and hands it to the installer. - It never interrupts: the dialog does not appear while the ECU is running.
4. What was measured
- With the Astra's flash and EEPROM, no node, 8 s of idle: U0121 is reported. With the
abs-nodeplugin: no U0121; it hears 3250 frames in 5 s and reads 750 rpm. - A write outside RAM is refused and logged; a plugin looping in
tickis stopped, the
others keep running. - A private GitHub source serves its index with a token and gives a helpful 404 without one.
- On the Android emulator: install from a file, from an HTTPS source, load at power-on
across a power cycle; the update detector found a newer release, downloaded its APK and
requested the install permission.
5. Upgrading
- Windows: unzip over the old folder.
- Android: install over 0.3.x; same key, same data.
Checked against an Opel Astra J 1.7 CDTI (A17DTJ) in the emulator, and on a Pixel Android
emulator (API 36, x86_64). Windows programs built by the release workflow from the tagged
commit, checked to import no Visual C++ runtime. Release APKs built from the tagged commit
and signed with the same key as 0.3.x (certificate SHA-256 5ff00c0a…f515c2).
Beta v0.3.2
🚀 sh705x-emu Beta v0.3.2 — the condition 65 fault codes share
The Astra's condition 0, which 65 of its fault codes need before their results count, read
from the firmware and described on each code that needs it. On Windows and on Android.
Context: one commit on top of 0.3.1, checked against the Astra J's own flash
(A17DTJ, Denso, SH7059, NCLSW420AX36).Theme: 0.3.1 showed which of a code's conditions are met; this one says what the most
common of them is. It is the ECU's go-ahead for its electrical tests: the ignition on, the
battery in range and the start-up over. Without it, a low battery would read as sensor
circuits out of range.Deliverables:
sh705x-v0.3.2-beta-windows-x86_64.zip:sh705x-gui.exeandsh705x.exe, which
carry their own C runtime.app-arm64-v8a-release.apkandapp-x86_64-release.apk: versionCode 5, versionName
0.3.2. Signed with the same key as 0.3.1, so they install over it.No firmware comes with any of them.
1. Condition 0
65 of the Astra's 202 codes need it, and 36 need nothing else. They are mostly the circuit
high and low tests of the sensors:
- the MAF, the coolant and intake temperatures;
- the pedal, the boost and barometric pressures;
- the rail pressure, the fuel temperature and level;
- the EGR position, the 5 V references;
- the exhaust temperatures, the DPF pressure sensor (P2455 among them).
The flag is set when all three of these hold:
- The battery between 10 and 16 V. With 1 V of hysteresis: it comes in between 11 and
15 V, and drops out under 10 or over 16. Measured: started at 9.5, 10.5, 15.5 or 16.5 V
it is never met; at 12 or 14 V it is. - The ignition on, and the start-up over:
- The ignition: input AN14 after a 4.01 divider, on above 4.5 V and off below 4.0,
two readings alike in a row, held a few runs after it drops. - The start-up: 56 runs in a row, about 0.8 s, after the start-up routine sets
port D's PD4.
- The ignition: input AN14 after a 4.01 divider, on above 4.5 V and off below 4.0,
- A third part that is always met on this calibration: it waits for a flag or for the
engine turning, but both its delays are calibrated to 0.
At idle, and with the ignition on and the engine still, it is met 1.06 s after power-on.
2. In the emulator
- Each code that needs condition 0 says what it is, beside whether it is met now (as
condition 1 already did for P0101). - Tests:
- it is met once the DPF fault has been provoked;
- with the battery at 9.5 V it is never met.
3. Upgrading
- Windows: unzip over the old folder.
- Android: install over 0.3.1; same key, same data.
Checked against an Opel Astra J 1.7 CDTI (A17DTJ): its flash at warm idle and with the
ignition on in the emulator, the battery set from 9.5 to 16.5 V, the flag's writes watched.
Windows programs built by the release workflow from the tagged commit, checked to import no
Visual C++ runtime. Release APKs built from the tagged commit and signed with the same key as
0.3.1 (certificate SHA-256 5ff00c0a…f515c2).
Beta v0.3.1
🚀 sh705x-emu Beta v0.3.1 — why a failing test is not yet a fault
The fault codes page now tells the tests the ECU counted from those it threw away, and
which of a code's conditions are met. The fault code tables are also found in the flash
from its code rather than looked up by calibration. On Windows and on Android.
Context: one commit on top of 0.3.0, checked against the Astra J's own flash
(A17DTJ, Denso, SH7059, NCLSW420AX36).Theme: in 0.3.0, P0101's test failed at every run at idle and the code never went
pending. This version shows why: the ECU only counts a code's results while its
conditions are met, and P0101's need 1500 to 4000 rpm among others.Deliverables:
sh705x-v0.3.1-beta-windows-x86_64.zip:sh705x-gui.exeandsh705x.exe, which
carry their own C runtime.app-arm64-v8a-release.apkandapp-x86_64-release.apk: versionCode 4, versionName
0.3.1. Signed with the same key as 0.3.0, so they install over it.No firmware comes with any of them.
1. The headline
| 0.3.0 | 0.3.1 | |
|---|---|---|
| A code's tests | how many ran and failed | and how many the ECU counted |
| Its conditions | — | each one, and whether it is met now |
| The tables | known for the Astra's calibration only | found from the code, on any flash laid out the same way |
2. Counted or thrown away
- The conditions: every test reports through one routine. That routine only passes the
result on to the one that counts it (the routine that reads tables A, B and C) when the
code's conditions are met. A code lists up to three, each a flag in RAM. - What the emulator shows: it probes the counting routine as well, so each code shows
how many of its results counted, and its conditions as they are now: met or not. - P0101 at idle:
- its test compares the air per stroke, brought to 25 °C and 101.325 kPa, with two maps
over engine speed; - at idle it is under the lower limit and the test fails; at 2000 rpm it passes;
- its results only count while condition 1 is met: 1500 to 4000 rpm, five other
quantities under their limits and condition 0 met, 125 times in a row; - so at idle it fails and is never counted.
- its test compares the air per stroke, brought to 25 °C and 101.325 kPa, with two maps
3. The tables, found
The fault code tables are no longer a constant for one calibration:
- The counting routine: a function that loads three tables of n words one after
another (A, B and C). - The reporter: the routine that calls it, called from many places with a constant
index under n. - The codes: the table of n words that looks like codes and is loaded from the most
places. - The reactions and the conditions: tables loaded by routines the reporter calls.
On the Astra's four dumps (the original, two workshop files and a later read) it finds
exactly what this project found by hand, in about 56 ms. It still does with half the codes
disabled. No other calibration has been tried: on one laid out differently it may find
nothing, and the page says so.
4. The rest
- Screenshots: the faults page on Windows and on Android, and the Android
oscilloscope with its time axis as the code now draws it. - Tests:
- the layout found on the Astra's flash, also with codes disabled, and none on a flash
without the code; - P0101 failing at idle, uncounted, condition 1 not met and 2 met;
- P2455 counted once its fault is provoked.
- the layout found on the Astra's flash, also with codes disabled, and none on a flash
- CI builds and tests both, as before.
5. Upgrading
- Windows: unzip over the old folder.
- Android: install over 0.3.0; same key, same data.
Checked against an Opel Astra J 1.7 CDTI (A17DTJ): its flash at warm idle in the emulator,
and at 2000 and 2500 rpm with the plants held. Windows programs built by the release workflow
from the tagged commit, checked to import no Visual C++ runtime. Release APKs built from the
tagged commit and signed with the same key as 0.3.0 (certificate SHA-256 5ff00c0a…f515c2).
Beta v0.3.0
🚀 sh705x-emu Beta v0.3.0 — fault codes live, and an oscilloscope on the pins
The calibration's fault codes with their tests running live, and an oscilloscope on the
ECU's pins: the inputs, the PWM outputs and the injector channel, held on an edge and
measured. On Windows and on Android.
Context: two commits on top of 0.2.0, checked against the Astra J's own flash
(A17DTJ, Denso, SH7059, NCLSW420AX36).Theme: 0.2.0 showed what the firmware reads; this one shows what it tests and what
it drives. A fault code is no longer just a line on a scan tool: you see its test run,
fail, and become the code the ECU reports, and the maps that test reads. And the PWM the
firmware computes is no longer a percentage: it is a waveform on a time base.Deliverables:
sh705x-v0.3.0-beta-windows-x86_64.zip:sh705x-gui.exeandsh705x.exe, which
carry their own C runtime.app-arm64-v8a-release.apkandapp-x86_64-release.apk: versionCode 3, versionName
0.3.0. Signed with the same key as 0.2.0, so they install over it.No firmware comes with any of them.
1. The headline
| 0.2.0 | 0.3.0 | |
|---|---|---|
| Fault codes | the ones the ECU reports, over OBD | all 202, enabled or not, each test live |
| A code's test | — | how often it runs, how often it fails, where, which maps it reads |
| Stopping on a code | — | "Stop at its next test" |
| The ECU's outputs | duty percentages in the engine view | waveforms, on 10 µs to 50 ms a division |
| Measuring a pin | — | frequency, period and duty per channel, an edge trigger, a cursor |
2. Fault codes, live
- From the flash: on the Astra's calibration the table holds 202 codes (194 enabled).
Each comes with:- its tables A, B and C (A and C both 0 disable a code, as a workshop file does);
- what the ECU does while it is active (raising the idle, among others).
- Their tests, probed: every test reports through one routine, with the code's index
and its result (0 passed, 1 failed). The emulator probes it alongside the maps and
counts, per code, the tests, the failures, the last result and where it was tested
from.- At idle 193 of the 202 are tested.
- With the DPF sensor at the top of its range, P2455's test starts failing and the ECU
reports the code.
- Where each is tested: the function that reports it, read from the code, and the maps
that function reads, each a link to the map. For example, P0101 (MAF range or
performance) reads two. - "Read the codes" asks the ECU for its confirmed (03) and pending (07) codes.
- "Stop at its next test" stops where the firmware next reports that code's test.
- Descriptions: the emulator brings them for the codes this project has met, in its
own words, and your own file can add the rest.
3. An oscilloscope on the pins
- Inputs (crank, cam, MAF): every edge at the cycle the chip took it.
- PWM outputs of ATU-II channels 6 and 7 (SCV, throttle, VGT, swirl flaps, EGR): drawn
from the timer's counter, period and duty. Exact between register changes, which are
placed when the emulator next looks (half a millisecond at most in real time). - The one-shot outputs of channel 8, where the injectors are: each pulse, exact.
- Controls:
- time base from 10 µs to 50 ms a division;
- a trigger on either edge of any channel (auto, normal or single, with how much comes
before it); - hold, and a cursor that reads the time from the trigger;
- each channel's frequency, period and duty.
At idle on the Astra:
| Signal | Measured | Check |
|---|---|---|
| Crank | 750 Hz, 50 % | 60 teeth at 750 rpm |
| SCV | 250 Hz, 53.5–54.8 % | the duty its registers give |
| VGT | 300 Hz | — |
| EGR opening drive | 200 Hz | — |
4. On Windows and on Android
- Windows: "Scope" opens the oscilloscope, and the maps window has a second page,
"Fault codes". - Android: two new tabs, "Faults" and "Scope". The scope's traces come first and the
controls below them.
5. The rest
- Tests for the new parts:
- the code table and its reports against what this project found by hand;
- a provoked fault seen tested, reported and stopped at;
- PWM traces and their measures, on a synthetic waveform and on the Astra's SCV;
- the windows and the app's frames drawn and read.
- A bug the tests caught: a PWM period that had started before cycle 0 overflowed. It
is worked out signed now. - CI builds and tests both, as before.
6. Upgrading
- Windows: unzip over the old folder.
- Android: install over 0.2.0; same key, same data.
Checked against an Opel Astra J 1.7 CDTI (A17DTJ): its flash at warm idle in the emulator,
with a fault provoked on the DPF sensor. Windows programs built by the release workflow from
the tagged commit, checked to import no Visual C++ runtime. Release APKs built from the tagged
commit and signed with the same key as 0.2.0 (certificate SHA-256 5ff00c0a…f515c2).
Beta v0.2.0
🚀 sh705x-emu Beta v0.2.0 — the calibration maps, as the firmware reads them
Every map the ECU reads, drawn whole from its own descriptor, with where the firmware is
on it right now, where each of its inputs comes from and where its result goes: on
Windows and on Android, live, in slow motion, or a lookup at a time.
Context: four commits on top of 0.1.0, checked against the Astra J's own flash
(A17DTJ, Denso, SH7059, NCLSW420AX36).Theme: 0.1.0 let you watch the engine; this one opens the calibration. A remap tool
shows a map as a table; here you see the firmware read it: which cell, from which line
of code, with which variables, and what it does with the result.Deliverables:
sh705x-v0.2.0-beta-windows-x86_64.zip:sh705x-gui.exeandsh705x.exe, which
carry their own C runtime.app-arm64-v8a-release.apkandapp-x86_64-release.apk: versionCode 2, versionName
0.2.0. Signed with the same key as 0.1.0, so they install over it.No firmware comes with any of them, and no catalogue of names either: those you
load from your own files.
1. The headline
| 0.1.0 | 0.2.0 | |
|---|---|---|
| Calibration maps | listed by sh705x touched, on the command line |
drawn live in the window and the app: heat map, surface, table, curve |
| Where the firmware is on a map | — | the cell it reads, now, and the trail of its last lookups |
| Where a value comes from | — | each input traced to a variable, a routine or another map |
| Names | — | the sensors found by moving them; an opendash profile read at run time |
| Stopping on a map | a breakpoint by hand | "Stop at its next lookup" |
2. Every lookup, without stopping the ECU
Denso firmware reads its maps through a few interpolation routines that take the map's
descriptor in R4 and its inputs in FR4 and FR5, and return the result in FR0. The
emulator now:
- finds those routines in the flash by itself, by counting which call targets get a
valid descriptor: on the Astra,0x7128(3D) and0x70B4(2D), the same the map
catalogue's Ghidra tools found; - probes them without stopping the machine: every call is recorded with its
registers on the way in and FR0 on the way out. With the maps open it runs about 6 %
slower (1.23 → 1.16 times real time, best of six runs each); closed, the run loop is
the same as before; - reads each map from its own descriptor, in physical units: sizes, axes, the cell
type (float, u8, u16, s8, s16) and the scale and offset it carries.
Checked: at idle the Astra reads 594 maps, all decoded, and on all 18 352
lookups of half a second the result the firmware gets is exactly the decoded map's
interpolation.
3. Where each value comes from
For each place a map is read from, the code around the call is read: back from the call
to where each input register was loaded, and forward to where the result is stored. An
input is a variable in RAM, the result of a routine (the engine speed), or the result of
another map, which becomes a link to it; and each map lists the maps that read what it
gives.
Checked live: stopped at 5000 real lookups across 616 call sites, 2617 of the 2625
variables named as an input hold exactly the value the firmware passes (99.7 %). The
others are routine results (mostly the engine speed) or other maps: the chain from
"fuel → torque" (09CEB8) into the next map shows up on its own.
4. Names
- With the emulator: what this project found in the Astra's calibration (the engine
speed routine, the torque and fuel asked, the setpoints of the engine view), used only
when the flash is that calibration. - "Find the sensors": every sensor is moved one way and the other for a moment, and
the variables that follow it in all three states take its name: 16 sensors on the
Astra. The 5 V references and the oil switch are left alone, and the engine is put
back: it idles where it did and no fault is left behind (checked). - "Read an opendash profile…": a parameter profile (opendash's
profiles.txt); the
emulated ECU is asked for each identifier in three engine states, and the variable that
holds the answer every time takes its name. Checked against two this project found
another way: Desired Fuel Rail Pressure lands onFFFEF924and Torque Delivered on
FFFEFC4C. Parameters that read 0 without injection cannot be placed. - "Read map labels…": labels by descriptor.
Profiles and labels are read from your disk at run time and never kept with the
emulator: such catalogues are often someone else's data.
5. On Windows
"Maps" opens a window with the list (filter by address, name or 2D/3D; follow the latest
lookup), the map in four views, its call sites as links into the code view, and "Stop at
its next lookup", which runs the ECU and stops where the firmware next reads that map, to
go on an instruction or a tooth at a time.
6. On Android
A "Maps" tab beside the engine: the same list, the map as a heat map (tap a cell for its
value), a surface turned with a finger, a table or a curve, where each value comes from,
the names, and "Stop at its next lookup". The maps are traced only while the tab is open.
7. The rest
- Tests for the new parts: the probes on a hand-written program; descriptors read and
refused; the masters, the decoding and the call sites checked against the Astra's
flash; a stop at one map passing all the others; naming that leaves no fault; the map
window drawn in every view; the app's map layout on the JVM. - CI builds and tests both, as before.
- The repository is public, under GPL-3.0-or-later.
8. Upgrading
- Windows: unzip over the old folder.
- Android: install over 0.1.0; same key, same data.
Checked against an Opel Astra J 1.7 CDTI (A17DTJ): its flash, at warm idle in the
emulator, and its own diagnostic answers for the profile's identifiers. Windows programs
built by the release workflow from the tagged commit, checked to import no Visual C++
runtime. Release APKs built from the tagged commit and signed with the same key as 0.1.0
(certificate SHA-256 5ff00c0a…f515c2).
Beta v0.1.0
🚀 sh705x-emu Beta v0.1.0 — an engine ECU's own firmware, running without the car
The first release: an emulator for Denso engine ECUs built on the Renesas SH705x, which
runs the ECU's own firmware, unmodified, on a simulated engine, on Windows and on Android.
Context: the first target is the ECU of an Opel Astra J 1.7 CDTI (A17DTJ, Denso,
SH7059, software NCLSW420AX36). Everything below was checked against that car's own
flash and EEPROM, and where the car could answer, against the car.Theme: from a CPU core to an engine that idles. The firmware boots, talks
diagnostics on its CAN bus, closes its own loops through simulated actuators, and holds
the engine at idle on the torque it asks for, as the car does.Deliverables:
sh705x-v0.1.0-beta-windows-x86_64.zip:sh705x-gui.exe(the window) and
sh705x.exe(the command line). They carry their own C runtime: they run on any
64-bit Windows with nothing else installed.app-arm64-v8a-release.apk(phones) andapp-x86_64-release.apk(the Android
emulator, x86 devices): versionCode 1, versionName 0.1.0, minSdk 26, English and
Spanish. Signed with the same key as opendash and Rustify.No firmware comes with any of them. Bring your own flash dump (1.5 MB) and, if you
have it, the EEPROM dump (8 KB).
1. The headline
| What it does | Checked against | |
|---|---|---|
| Core | Every SH-2E instruction, the FPU rounding toward zero as the chip does | Ghidra's disassembler on all 65 536 codes, an exact rational oracle, mutation testing |
| Boot | The Astra's flash from reset to its background loop | the same 196 669 register accesses as Ghidra's emulator |
| Diagnostics | OBD and GMLAN over its emulated CAN bus | the car: 231 recorded answers come back with the same shape, 108 byte for byte |
| Engine | Idles on the firmware's own torque | the car at warm idle: 750 rpm, 12 N·m, 7.0 mm³, EGR a third open, 5.2 g/s of air |
| Speed | 1.26 to 1.36 times faster than real time | i7-10870H, with the EEPROM, CAN and the engine turning |
2. The ECU, emulated
- The chip. The SH-2E core, and the on-chip peripherals the firmware uses: the
interrupt controller, timers (CMT), DMA, serial ports, the A/D converter, HCAN-II and
the ATU-II's input captures, tooth channel, one-shot pulses and PWM outputs. The
firmware boots with no status bit forced. - The board. The four 93C86 EEPROMs, with the order of their blocks proven from a
record that only fits one way; the status chips that watch the output stages, so no
fault is pending at idle; the injector driver on SCI0. A PCMflash dump is served as it
is. - The car around it. A 60-4 crank wheel and a 4+1 cam wheel, every sensor in
physical units through the curve the firmware itself applies, the MAF as a frequency,
and the other nodes on the bus (the ABS, and the rest of the car standing still).
3. An engine that idles on its own
Plants for the suction control valve, the EGR valve and the fresh air it leaves, and the
turbocharger close the firmware's own loops. With them, a simulated crankshaft turns on
the torque the firmware asks for, and its idle governor settles where the car does:
| Emulator | The car, warm idle | |
|---|---|---|
| Engine speed | 750 rpm | 750 rpm |
| Torque / fuel asked | 12.0 N·m / 7.02 mm³ | 12.0 N·m / 7.0 mm³ |
| EGR / fresh air | 33 % open / 5.2 g/s | a third open / 5.2 g/s |
| SCV current | 2249 mA for 2250 asked | on its setpoint |
4. Diagnostics, from your usual tools
- ELM327 over TCP (
sh705x serve): any diagnostic app that talks to an ELM327 over
Wi-Fi can be pointed at the emulator. - An emulated Scanmatik SM3 (
sh705x sm3): opendash connects to it as it would to
the real adapter. - A console in the window, in virtual time.
OBD modes 01, 03 and 09 (the VIN, over ISO-TP) and GMLAN service 1A answer as the car
does. Faults can be provoked: an output stage reporting a short (--status), a sensor out
of range (--adc).
5. Comparing calibrations
sh705x mapsruns the same speed × pedal grid against two flash images, on the
plants, and shows where the second differs: fuel and torque, rail pressure, SCV, air
flow, boost, VGT, EGR, throttle, load.sh705x touchedlists the maps the firmware reads in a scenario, with their inputs
and results, named in words, marking those a second image changes.
6. Watching it work
- The window. It opens on a load screen: choose the images with the system's dialog,
type their paths, or drop the files onto it. Then registers, the code around the PC
with breakpoints, memory, CAN traffic, interrupts, sensors as sliders, and the console. - Step by step. An instruction, a crank tooth or an input edge at a time, and slow
motion down to a thousandth of real time. - The engine view. The crank and cam wheels turning under their sensors, the four
cylinders, and charts in ECU time of what the firmware asks for against what the plants
give back. Each column keeps its minimum and maximum, so a move faster than a pixel
still shows: the EGR drive stepping every few milliseconds is there at full speed.
7. On Android
The same core, in Kotlin and Compose: the engine view on a phone.
- Loading. Pick the dumps with the system's file picker. The app asks for no
permissions and keeps the dumps in memory only. - Running. "Idle on its torque" starts the engine as on the desktop; slow motion,
"Next tooth" and "Next edge" work the same. - Theme. It starts in a dark theme; light, or the system's, is a tap away.
On the Android emulator (x86_64, software GPU) it holds 750 rpm at 0.66 times real time.
How fast it runs on a real phone has not been measured yet.
8. What it does not do, and why
- It does not inject. The ECU will not inject until the immobilizer authorizes it,
and that side is not emulated, by choice: the emulator shows what the ECU does
without that authorization, as with a key it does not recognise. The rail pressure is
therefore the sensor as set, not a combustion, and with the car's EEPROM the idle rises
to 1000 rpm (P160D). - The cylinders' phase is assumed. Where cylinder 1's top dead centre falls relative
to the gap is not known; the engine view says so. - Not yet: checking it against a real ECU on an FPGA-driven bench, and the ECU itself
on an FPGA.
9. Installing
- Windows: unzip and run
sh705x-gui.exe.sh705x.exe helplists the command line. - Android: install the APK for your device. It is the first version, so there is
nothing to update from. - From source:
cargo build --release; the Android app with./gradlew :app:assembleReleaseinandroid/, with the NDK and cargo-ndk (see the README).
Licensed GPL-3.0-or-later.
Checked against an Opel Astra J 1.7 CDTI (A17DTJ): its flash and EEPROM dumps, a
diagnostic sweep of the car's engine ECU and its GMLAN identifiers recorded from the car,
and its live values at warm idle. Windows programs built by the release workflow from the
tagged commit, checked to import no Visual C++ runtime. Release APKs built from the tagged
commit, signed with opendash's and Rustify's key (certificate SHA-256
5ff00c0a…f515c2, the same in all three), installed and started on the Android
emulator.