Tag Archives: Euro6

DPF Light Patch

This is meant for programmers or at least folks who understand coding in general. Here I am going to show how I implemented the DPF light patch, part of Diesel ECU Patch v1, on my former (Euro 4) car.
DPF Light Patch - Active Regeneration In Progress


Actual source code, updated to C++14:


// Copyright SubaruDieselCrew (2011-2017)   https://subdiesel.wordpress.com

#include <array>
#include <chrono>
//#include <bitset>
#include "sh.h"
#include "JZ2F401A.h"

using namespace std::chrono_literals;

/**
 * @brief DPF light flashing modes (stock)
 *
 */
enum class DPFLightMode {
    off = 0,
    /**
     * @brief soot-high warning aka vehicle speed request
     *
     */
    on_steady = 1,
    /**
     * @brief error
     *
     * (multiple causes: compulsory regeneration required, oil dilution critical, ash overfill, DPF limp-home mode;
     * see https://subdiesel.wordpress.com/2011/03/21/dpf-light/ )
     */
    flashing = 2,
};

/**
 * @brief time resolution (= CAN frame ID 0x600 interval)
 *
 */
const constexpr auto interval {50ms};
/**
 * @brief stock period for flashing mode is 800 ms,
 *        does not have to match stock here
 *
 */
const constexpr auto dpfLightPeriod {800ms};
/**
 * @brief defines DPF light output over time when active regeneration is on
 *
 */
const constexpr std::array<bool, dpfLightPeriod / interval> dpfLightCustomPattern
{
    1, 1, 0, 0,  1, 1, 0, 0,
    0, 0, 0, 0,  0, 0, 0, 0
};
// tested alternative: bitset; storage-efficient but much more lookup code
// const std::bitset<dpfLightPeriod / interval> bits {"1100110000000000"};

/**
 * @brief Implement custom flashing mode.
 *
 * Called every 50 ms (CAN-ID 0x600 interval)
 * from patched stock function "calcDpfLight".
 * Standard error flashing mode already handled by untouched
 * stock subroutine portion and this case this function won't get called.
 */
void calc_DPFLight_continue()
{
    // needed as original functionality has been overwritten for hook instructions
    if (DPFLightMode(*DPFLightModeEnum_b) == DPFLightMode::on_steady)
    {
        *DPFLight_bool = true;
        return;
    }

    // at this point DPFLightMode == DPFLightMode::off
    if (!*DPF_Regeneration_bool_SSM)
    {
        *DPFLight_bool = false;
        return;
    }

    // at this point active DPF regeneration is ON, do custom flashing
    // reusing DPF light counter var is safe
    auto counter = *DPFLightCounter_b;
    if (++counter >= dpfLightCustomPattern.size())
        counter = 0;
    *DPFLightCounter_b = counter;
    *DPFLight_bool = dpfLightCustomPattern.at(counter);
}



// Copyright SubaruDieselCrew (2011-2017)   https://subdiesel.wordpress.com
/*
	For stock ROM:
	Model	2009/2010 Impreza Turbo Diesel 2.0 6MT EDM 110 kW / 150 PS
	ROMID	6644D87207
	CID		JZ2F401A
	CVN		F5AD7142 FB841734
	PAK		22611AP283
*/

#ifndef JZ2F401A_H
#define JZ2F401A_H

#include "diesel_rom.h"

// RAM vars
static auto const DPFLight_bool = reinterpret_cast<volatile bool*>(0xFFFF9C1E);
static auto const DPFLightModeEnum_b = reinterpret_cast<volatile int8_t*>(0xFFFF9C1F);
static auto const DPFLightCounter_b = reinterpret_cast<volatile uint8_t*>(0xFFFF9C53);
static auto const DPF_Regeneration_bool_SSM = reinterpret_cast<volatile bool*>(0xFFFFB222);
…


SuperH disassembly using objdump which is part of GNU binutils. Binary had been generated by GCC.


void calc_DPFLight_continue()
c:  91 1d   mov.w   0x4a,r1 ! 9c1f
e:  60 10   mov.b   @r1,r0
10:  88 01   cmp/eq  #1,r0
12:  8d 17   bt.s    0x44
14:  71 ff   add     #-1,r1
16:  91 19   mov.w   0x4c,r1 ! b222
18:  61 10   mov.b   @r1,r1
1a:  21 18   tst     r1,r1
1c:  8d 0e   bt.s    0x3c
1e:  62 13   mov     r1,r2
20:  91 15   mov.w   0x4e,r1 ! 9c53
22:  e2 0f   mov     #15,r2
24:  61 10   mov.b   @r1,r1
26:  71 01   add     #1,r1
28:  61 1c   extu.b  r1,r1
2a:  31 26   cmp/hi  r2,r1
2c:  8f 02   bf.s    0x34
2e:  60 13   mov     r1,r0
30:  e0 00   mov     #0,r0
32:  e1 00   mov     #0,r1
34:  92 0b   mov.w   0x4e,r2 ! 9c53
36:  22 10   mov.b   r1,@r2
38:  d1 06   mov.l   0x54,r1 ! 945c0
3a:  02 1c   mov.b   @(r0,r1),r2
3c:  91 08   mov.w   0x50,r1 ! 9c1e
3e:  21 20   mov.b   r2,@r1
40:  00 0b   rts
42:  00 09   nop
44:  21 00   mov.b   r0,@r1
46:  00 0b   rts
48:  00 09   nop
4a:  9c 1f
4c:  b2 22
4e:  9c 53
50:  9c 1e
54:  00 09
56:  45 c0

5c0:  01 01   .word 0x0101
5c2:  00 00   .word 0x0000
5c4:  01 01   .word 0x0101
5c6:  00 00   .word 0x0000
5c8:  00 00   .word 0x0000
5ca:  00 00   .word 0x0000
5cc:  00 00   .word 0x0000
5ce:  00 00   .word 0x0000


As you can tell there is not much code required. Much more work, orders of magnitude (!), is necessary to reverse-engineer the related stock ROM portions in the first place, defining functions, disassembling machine instructions, naming local and global variables etc.

Usually, ROM and RAM addresses depend on the actual ROM version used. Above definitions work for outdated CID JZ2F401A (dated 2009-Sep). Apart from ROM specific variable addresses the same code will work for all known Euro 4/5/6 models.

Resultant binary data is meant to be inserted into a free unused ROM region. On Renesas SH microprocessors (i.e. SH7058S) free ROM space is rather easy to find – just look for big chunks of continuous FF-bytes. This is because those chips erase bytes to value 0xFF. Others, e.g. Infineon TriCore series, erase their internal flash ROM to zeroes instead.

To actually make use of the added logic, I had to modify (patch) a few bytes in the original calc_DPF_light subroutine, so that after doing some of its work it will call my own function, knowing its start address (0x9400C). Usually there is no free space in between stock functions, therefore we have to apply clever patching tricks to make room for a few new instructions and/or divert execution flow.

Finally, after carefully verifying the changes applied to the stock ROM, you have to correct checksums. Flashing software usually does this anyway, perhaps asking first. Checksum correction and verification is actually very easy to do for such Denso firmware.

Providing SDC-modified ROMs is possible, however will not be free due to amount of labour involved. Contact us if you’re interested.

Updates

  • 2017-04: minor update, actual disassembly
  • 2016-11: updated source to C++14
  • 2016-10: updated source to C++11 with Doxygen documentation
  • 2016-01: added disassembly

Injector Codes

Denso Injector DCRI107890 (Denso part# 095000-7890, Subaru part# 16613-AA020) for Euro 4 Subaru Diesel

Introduction

Quote from a DENSO document incl. picture below:

Replacing a Diesel Common Rail Injector:
When replacing a DENSO Diesel Common Rail Injector, marked with a compensation code, it is necessary to register the ID code, printed on the upper part of the injector, with a genuine OEM – or DENSO diagnostic tool, into the electronic control unit (ECU). The injector compensation (ID) code is used to compensate injector production tolerances.
Some vehicles also require Small Injection Quantity Learning.

Denso Injector ID graphic

Subaru’s term for injector compensation or ID code is “injector code“. Dealership diagnostic software (SSM-III, SSM4) has menu items like “Injector Code Display” and “Injector Code New Registration (SSM to ECM)

First off, the engine control unit has no way of measuring actual injected fuel amount, such technology would be expensive for these kind of small fluid quantities. Due to high common rail pressure, even tiny production tolerances result in unwanted injection quantity variations. Obviously, each injector must be registered using its correct cylinder number so the ECU can apply individual adjustments when it is calculating injections. Basically, the ECU accomplishes desired (target) injection quantity by adjusting the duration of injector drive signal.

Since programmed injector codes as well as any other important data is being saved into an extra EEPROM chip, there is no risk of data loss having the car battery disconnected.

Consequences of Wrong or Missing Injector Codes

According to DENSO:

  • Knocking noise
  • Unstable idle
  • Wiggling during driving
  • MIL (Check Engine Lamp) on

Cylinder numbers – Quick Reference

As for a quick reminder, looking at the front of the car into engine bay, cylinder numbers are:

towards back
transmission
  3        4
  1        2
car front (radiator etc.)

Getting Injector Codes via QR Code

Although normally this should not be necessary, injector codes can be read from the actual injector parts even when mounted on the engine and inside the engine bay, therefore not easily accessible. Taking a picture using a mirror tool is relatively easy, no need to disassemble any parts:

Injector QR Cyl2 small

While the injector code label itself is hidden by the (white) electrical connector, its QR code is visible by default.
Make sure the QR code on the photo is as sharp as possible and has sufficient resolution. At the car I usually just try to get high quality pictures. Later on my computer I simply select the best pic, then scan QR info straight off the computer screen using a smartphone.

Using an image manipulation program (i.e. GIMP, PhotoShop) in order to improve the QR code area can result in much better QR detection. In my case, this was not needed as the app can also detect inverted QR (light code on dark background).
Android app tested: “Barcode Scanner” from F-Droid repository. Check settings → Invert scan.

As an example, the following pic is the extracted and improved QR code portion from above picture. I used these steps in GIMP: crop, perspective correction, grayscale, invert, brightness & contrast . You should be able to scan this:

Injector QR Cyl2 processedScanning captured QR code results in a line of text containing 49 characters:

7890AA0200809118736B30000000000E9EBEBECF300000045

First 19 chars:

7890AA0200809118736
  • First four chars 7890 match Denso basic part number.
  • AA020 is also found in Subaru part# 16613-AA020
  • Possibly contains production date 2008-09-11 ?
  • Serial# ?

Injector Code Format

Remaining 30 chars is the exact injector code needed for ECU. These chars must be in hexadecimal [0-9, A-F] form as they are transmitted as 15 bytes to and from the ECU:

B30000000000E9EBEBECF300000045
Byte index [0..14] Content
0 const, sort of generation ID, verified by ECU:

  • Euro 4: B3
  • Euro 5: B2
  • Euro 6: B6
1..12 12 payload bytes containing correction data, type int8, for up to 12 pre-defined correction points. Each signed byte must be within range [-125, 125]. In other words, the following five bytes are not allowed: 7E, 7F, 80, 81, 82.
13 const 00
14 simple XOR checksum

Resultant properly formatted injector code (left to right, 4 chars per block):

B300    0000
0000    E9EB
EBEC    F300
0000    45

Notes

Invalid codes will be rejected! Euro 5+ will return NRC 0x31 (request out of range).
Injector code data is not compatible between common rail system generations due to differing correction points! Euro 4 uses fewer points (10) than E5+ (12). Therefore, for example, programming a Euro 5 injector code into Euro 4 ECU will not work and vice versa.

Software

AFAIK, Denso’s own PC diagnostic software has the ability to read from a QR scanner device, Subaru OEM application SSM-III does not – need to type in codes manually.

Protocols needed for injector code display & registration:

  • Euro 4: SSM2 via Serial
  • Euro 5/6: Extended OBD-II

As far as we know, there is no free/open-source software for this yet.

EcuTek tool probably supports injector coding (up to Euro 5 ?).

Other Brands

Many other car brands are based on Denso diesel software. These often use similar 15 byte injector codes as well. However, injectors and pre-defined correction points usually differ!

Mazda CX-5 (SkyActiv-D 2.2)

Piezo injectors! 15 bytes; first byte: 3C; pressure levels and corrections points differ vs. Subaru.

Updates

  • 2017-05: additional notes and details
  • 2016-04-03: Euro 6

Over-Rev

Tachometer driven by custom software, engine off, for demo purpose. Needle is at maximum position.

Tachometer redline range is 4,700 – 5,500 rpm. All related subroutines are well known by now. Same source code in all diesel ROMs it seems.

Within normal driving conditions the ECU won’t allow any over-rev because it gradually reduces injected fuel (zero at 4,800 rpm ?). This way over-rev can only be reached by force (mis-shift or faults).

Over-Rev Counters

There are two over-rev counters, triggered by two different RPM levels. Both are considered important therefore saved into EEPROM.

Item Unit MY08 MY09 MY10 MY11
High RPM: Repeated incidents might damage engine.
High RPM Threshold rpm 5,500
High RPM Hysteresis Low rpm 5,250 4,900
Very high RPM: Might damage engine on single occasion.
Very High RPM Threshold rpm 5,900
Very High RPM Hysteresis Low rpm 5,650 4,900

RPM must drop below hysteresis low value to allow next incident to be triggered. Prevents multiple counter increments on single incident.

Reaching five recorded “high” or a single “very high” incident might also turn on MIL.

DTC P0219 Engine Overrunning Failure

Triggered by condition: RPM ≥ 5,150. Confirmed with all models so far, Euro 4/5/6. Also causes freeze frame(s) capture.

High RPM History

Diesel specific, basically meant to acquire additional info. Subaru’s warranty department will probably look at this.

Although diagnostic software lists names like “Highest Over-Rev RPM history”, these actually start tracking within redline already (allowed but to be avoided), obviously don’t mean real over-revs when rpm < 5,500.

Only when above DTC P0219 has been triggered, the high-RPM-history management subroutine activates, tracking highest RPM it sees during incident. Later when DTC condition is not set anymore (=RPM dropped below 4,900) it updates its records, filling in highest engine speed it had seen plus current odometer value.

There are four records: highest and last three incidents.

Each record consists of two parameters and can be retrieved using a Subaru specific protocol:

  1. RPM, UInt8: x*20+4000 [rpm] → lowest possible value is 4,000 meaning NO incident yet!
  2. Odometer1, UInt16: x*5 [km]

Since history subroutine is separated from DTC checking subroutine and both use current RPM as input, it’s possible that when history sub gets to run, the RPM had already dropped < 5,150. So even though history needs the 5,150 rpm DTC threshold, remembered RPM values can be slightly lower, already seen that in practice.

1) It is actually the ECUs internal odometer, does not have to match dashboard odometer driven by combination meter computer! Usually these two odometer values drift off over time, typically 1% or more. Accessing Subaru diesel ECU odometer usually requires ROM specific software knowledge as there is no diagnostic protocol parameter implemented for this.

Injector Learning / Calibration

Applies to all known Boxer Diesel models, Euro 4/5/6.
Injector learning (micro quantity calibration) procedure can improve engine idling (vibrations, noise, fuel economy …). The ECU measures engine speed changes, derives learning values and uses them (for idling only?) from there on.

Tiny fuel injections, achieved through very short pulses, are especially sensitive to mechanical wear inside injectors. Remember, the ECU does not know how much fuel is actually being sprayed into the cylinders. However, by measuring acceleration (crankshaft speed changes) after at each cylinder’s power stroke, it can adjust injection pulses to achieve a balance across cylinders.

Normally, a power stroke consists of two or three individual injections, main injection always being the largest one. For micro quantity calibration, the software does things differently. It divides the necessary fuel to hold engine speed into more and evenly small injections so that only short pulses matter and differences in injected amounts can be compensated.
The entire procedure is done in two ways:

  1. Automatically by ECU when idling (~800 rpm), for example at traffic light stop. Takes roughly a minute to complete. Goes through first 4 pressure levels. ECU will try again if it could not finish.
  2. Compulsory using Subaru dealer software (open source solution is planned). In addition, this mode uses higher common rail pressure, revving the engine up to ~1,200 rpm at 5th pressure level which is not acceptable in auto-mode.

Injector Learning Chart

Either way, the operation causes distinct engine noise due to special injections, easy to recognize. Also altering common rail pressure to the next pressure level causes noise to change.

You can use logging (Subaru specific protocols) in order to monitor related parameters:

  • injector learning status (ON/OFF)
  • injection quantity learning values [ms] (4 cylinders times 5 pressure levels = 20 values; all zeroes means learning has not been completed successfully)
  • mileage after last learning [km]
  • mileage after injector replacement [km]

As usual, all relevant values are being saved into EEPROM.

Automatic Injector Learning Intervals

In theory, ECU wants to do automatic injector learning at these predefined intervals:

# Odometer
[km]
Interval
[km]
1 50 50
2 200 150
3 350 150
4 950 600
5 1,550 600
6 4,550 3,000
7 7,550 3,000

Confirmed by disassembly from Euro 4/5/6 software. As you can see, the interval is being increased up to 3,000 km.

Above odometer values are theoretical because:

  • For the ECU mileage after last learning matters, not any absolute odometer value.
  • If conditions are not met, learning process is being delayed, trying again at next occasion, increasing all further odometer numbers.
  • ECU internal odometer is not very precise, it will drift off from combination meter odometer over time.

Injector Learning Conditions

Basically, engine must be warm, around 80°C coolant temperature. Lots of parameters must be within pre-defined ranges (fuel temperature, accelerator not depressed, battery voltage 12..15 Volts, …).

Video Links

Courtesy of contributors posting their videos.

Automatic learning

Compulsory

Tested

  • Adjusting internal variable “mileage after last learning”, i.e. setting to 3000+ km triggers automatic calibration ASAP.
  • Adjusting ECU odometer, i.e. to re-synchronize with dashboard value.

Engine Oil Change (DPF models)

Owners want to be able to change oil by themselves obviously.
On closed-type DPF models (all except MY 2008/2009 Legacy/Outback which are mated with open-type DPF) you’ll need to reset oil dilution parameter as it is calculated (not measured!) by the ECU. Basically active DPF regenerations in particular cause diesel fuel bypass into engine oil. Piston rings cannot seal perfectly.
Not doing a reset could even cause limp-home-mode in the LONG TERM, IF the ECU assumes dilution being too high.

Following info as seen in all managed-DPF-ROMs so far.

DTC P1468 Oil dilution is being triggered when oil dilution ratio ≥ 10 %. Also results in DPF light flashing.

The engine management software assumes 6.13 kg oil mass and this maintenance operation sets RAM variable “Oil Dilution Amount [kg]” to zero.
The ROM’s internal calculation steps are:

  1. Oil dilution amount [kg]
  2. Oil dilution ratio [%] = 100 * OilDilutionAmount [kg] / 6.13 kg
  3. Estimated distance to oil change [km], via LUT

Also see posts Estimated Distance to Oil Change as well as Oil Dilution Graph.

Oil Dilution Reset

We’ve traced related Euro 4/5/6 ECU-ROM code. Here are all possible options:

Euro 4 (IV)

  1. Diagnostic protocol (SSM2 via Serial only). See post Oil Dilution Reset (K-Line, Euro4). There is no alternative method on Euro 4 models! ¹)

Euro 5 (V), Euro 6 (VI)

  1. Diagnostic protocol (Extended OBD-II only since it is not implemented via SSM2 in ROM)
  2. Manual procedure, check your owner’s manual. You only have 4 seconds to accomplish each item, starting from brake pedal:
    1. Transmission in neutral
    2. Apply parking brake
    3. Start engine
    4. Depress and hold brake pedal
    5. Defogger ON
    6. Lights ON (step 1 = parking/clearance/DRL lights is sufficient)
    7. Defogger OFF
    8. Lights OFF
    9. Defogger ON (again)
    10. Lights ON (again)
    11. Defogger OFF
    12. Lights OFF
    13. Release brake. Now the glow light (depicting yellow coil) will flash for a few seconds. Otherwise try again, repeat from 4th item.
      Glow Light

    Many users have confirmed this procedure, e.g. Subaru Forester Owners Forum – Oil change on Diesel engine and ECU service reset. We also know the related software subroutines, seems to work as advertised.

1) In theory, we could patch Euro 4 ROMs to add the same Euro 5/6 manual procedure. Not a trivial task, though, talk about hours of development and testing…