Tag Archives: DPF

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

Oil Dilution Reset (K-Line, Euro4)

Applies to

Old Euro 4 models, model years (MY) 2009/2010, fitted with closed-type DPF, (therefore Impreza and Forester only?). This also means using SSM2 via Serial (K-Line) protocol, as SSM2 via CAN will not work for most maintenance operations on such old ECUs due to ROM software limitation.

Description

After changing the engine oil, the ECU needs to be told in order to reset its oil dilution amount and ratio values – the ECU cannot detect the change by itself. By the way, Euro 4 engines do not even have an oil level switch/sensor. Also look at post Oil Dilution Graph describing ECU calculations.

By applying a little communication, dilution will be zeroed, also saved into the ECU’s EEPROM after ignition off. Otherwise this (estimated!) value might rise over time and trigger DPF light flashing having reached 10% ratio.

It seems there are still folks out there interested in this, changing oil by themselves, not at the dealership where mechanics use Subaru Select Monitor (SSM-III or newer SSM-IV) software.
I searched through some of my old notes and found the rather short and easy procedure.

Requirements

Working K-Line connection to ECU. See page SSM2 via Serial.

C-style pseudo code


// only one single SSM2 address is needed
const int address = 0x27D;
byte b = Read(address);

// set bit 6
Write(address, b | 0x40);

// Wait for some time (500 ms or so is more than enough for the ECU to do its work)
Sleep(500);

// clear bit 6
b = Read(address);
Write(address, b & ~0x40);

// verify, make sure bit 6 is zero again...
b = Read(address);

Almost done! Now turn ignition off so that new content gets saved into EEPROM! I also recommend waiting for about 20 seconds, then turn ignition back on in order to query oil dilution ratio [%] and/or oil dilution amount [g] parameter(s) to be sure. These values should be zero now. See post Why and when cycling ignition matters for explanation.

In RomRaider logger definitions (version 310) these parameters are called:

ID EN Unit DE
P193 Oil Dilution Ratio % Öl Verdünnung Verhältniss
P236 Cumulative oil diesel entry g Öl kum Dieseleintrag

SSM2 via K-Line communication example

SND = message from tester to ECU
RCV = received response from ECU


SND: 80 10 F0 05 A8 00 00 02 7D AC
RCV: 80 F0 10 02 E8 00 6A

SND: 80 10 F0 05 B8 00 02 7D 40 FC
RCV: 80 F0 10 00 F8 40 BA

SND: 80 10 F0 05 A8 00 00 02 7D AC
RCV: 80 F0 10 02 E8 40 AA

SND: 80 10 F0 05 B8 00 02 7D 00 BC
RCV: 80 F0 10 00 F8 00 7A

SND: 80 10 F0 05 A8 00 00 02 7D AC
RCV: 80 F0 10 02 E8 00 6A

Update

It has been confirmed to be working, using RomRaider‘s built-in Test tool (menu: Tools → Launch Test App…). Since the sequence of individual commands is not time critical, it can be done by sending SSM2 messages manually one after the other. See this RomRaider Forums thread.

Oil Dilution Graph

Shown graph contains a complete DPF active regeneration process – the rising section (750 seconds duration = 12.5 minutes).
Basically, oil dilution is driven by:

  1. post-injections during active DPF regeneration
  2. estimated diesel fuel evaporation

According to internal algorithm and logged data, normal operation does not increase dilution. Oil dilution slowly decreases as the ECU estimates fuel evaporation out of engine oil. (On Euro 4 at least, this algorithm is also running if ignition is on, engine not running but coolant temperature warm enough).
Active regeneration however uses one or more post-injections (small additional late injections – during exhaust stroke) in order to heat up the DPF, raising oil dilution amount, at much higher rate therefore winning over evaporation.
Notice these short intermittent steps during regeneration – these are caused by coasting – ECU suspending all injections, including post-injections.
Wouldn’t coasting cool down the DPF then by pushing rather cold air through the system? To mitigate this, the software fully opens EGR valve (70 deg). As soon as injections resume, EGR valve is being closed again. Normally, during active regeneneration it is in fully closed position (0 deg) helping to increase exhaust temperature (more oxygen).
If you look carefully, you can spot more evaporation going on after regeneration had finished compared to before it started. This is mainly due to higher engine temperature, having reached normal operating conditions of around 90 °C. For evaporation to get going it needs temperatures beyond 30 °C, the higher the better.
Also take a look at post “Estimated Distance to Oil Change” for additional information.

DPF Management

Some parts of Denso’s complex Diesel Particulate Filter (DPF) algorithms are somewhat known by now. Will update with new results from time to time.
Link to page

Diesel ECU Patch v1

Finally, now that we are able to reflash diesel ECUs it makes sense to do some changes – or improvements as we think of it.
Couple of things already implemented and working perfectly:

  • Added SSM2 getter function Engine Load, using standard definition 0x000007, x*100/255 [%] – unused in stock diesel ROMs. As engine load value is being calculated internally anyway, also available as OBDII Mode 1 PID 0x04, it makes sense to provide this in SSM2 protocol, too. Very easy to do actually, just a single function pointer change – 4 bytes.
  • Added SSM2 getter function Gear, standard def 0x00004A, x+1 [-], unused in stock ROMs. Tiny function required because of +1 offset.

Btw, as with all SSM2 functions, the physical layer does not matter – any SSM2 function gets called via both serial (Euro4) and CAN connection. Diesel ECUs typically have SSM2 virtual address space 0x000000-0x00034F. That’s 848 function pointers, plus same amount in SSM2-write vector table. You could hook up a lot non-standard things as there are plenty of unused addresses…

  • Modified SSM2 init capability bits accordingly. That way above additional params do show up automatically on ALL SSM2 capable software (RomRaider, FreeSSM, SSM3, DashDAQ ?, …).
  • DPF light active regeneration flashing mode. Applies to closed-type DPF models only. Stock behavior (steady on and error flashing) is untouched, we managed to append own additional code. New code part checks if any stock DPF light mode is currently active. If not, it checks DPF regeneration flag, and only if true it performs this new flashing mode. It’s particularly useful at low engine speed – warning driver that additional turbo lag can occur. Remember, engine management reduces boost and manifold air pressure – sometimes a lot – when active regen is in progress.
    Update: got very positive feedback on this one. Even on a bright sunny day looking through the windshield, that blinking light on dashboard gets noticed immediatly. Whole thing is only a screen page of easy C++. Kudos to GCC, v4.6.x generated code is awesome – highly efficient.

Right now this is all internal, experimental, Linux-centric stuff. Availability of our services to the public will be discussed. It meant many months of full-time work to get this far, call for open source collaboration (less work for everyone) did not work…

DTCs

Wikipedia: Diagnostic Trouble Code (DTC)
All following DTCs are Subaru specific, applicable to closed-type DPF Boxer Diesel models only, Euro 4, 5, 6 emission standards.
Some of these DPF related DTCs trigger DPF light flashing.

ID Name Comment
P1465 DPF Particulate Overfill
P1466 DPF Substrate Damaged (E4: never gets set?)
P1467, P242F DPF Ash Overfill ≥ 100 %
P1468 Oil Dilution ≥ 10 % ; also see Distance to Oil Change
P1469, P246C DPF Limp-Home Mode Reduces injection quantity and power.
P1471 DPF Pressure Difference Sensor Upstream Piping Trouble (E4: never gets set?)
P1472, P2454 DPF Pressure Difference Sensor (Low)
P1473, P2455 DPF Pressure Difference Sensor (High)
P147B Catalytic Converter and DPF
P2453 Particulate Filter Pressure Sensor “A” Circuit Range/Performance

Related ROM code has not been analyzed completely yet, no guarantees as usual. It is definitely much more sophisticated compared to Subaru petrol ROMs.

Updates

  • 2016-10 Added DPF related Euro 6 DTCs.

DPF Light

Notes

  • Do consult your car manual! Unfortunately owner’s manuals lack useful details.
  • Please also read our DPF management page.
  • Same DPF light logic in Euro 4/5/6 ROMs.
  • DTC numbers differ across boxer diesel generations.

Applies To

All Boxer Diesel models fitted with closed-type Diesel Particulate Filter (managed DPF), emissions spec Euro 4, 5, 6.

Therefore does NOT apply to early diesel models equipped with open-type DPF (MY 2008/2009 Legacy/Outback). These do not have DPF management at all in software, no sensors, no DPF light, a lot less to worry about!

Steady


Constant light means “Soot High Warning“, also called “Vehicle Speed Request“.

According to manual, one should drive for at least 15 minutes at 60+ km/h, providing favorable conditions (exhaust gas temperature high enough) for active DPF regeneration to start (and ideally complete) in order to reduce soot.

Steady light is triggered by single condition:

  • Soot Accumulation Ratio > 85%

According to ROM logic, steady light will turn off when soot drops below 75% (hysteresis).

If there’s any error condition, see below, the error has higher priority, always resulting in flashing mode.

Flashing


Flashing/blinking means “Error“, any of the four possible conditions can trigger this mode right away – without showing steady light before.
The manual usually states something like: go to Subaru dealership for inspection. Waiting too long might worsen the issue! Mileage driven with light ON can be recorded by ECU and read out with software!

  1. Oil Dilution Ratio ≥ 10% (also sets DTC P1468 Oil Dilution). Remember, as dilution is just a rough estimate, the value and therefore this error can easily be false! Might disappear when dilution drops below threshold due to calculated evaporation, see post Oil Dilution Graph. Dealership might have forgotten to reset oil dilution at oil change – seen this many times on forums.
  2. Compulsory regeneration required, also called “Dealer Visit Request“; e.g. Soot Accumulation ≥ 100%; ECU does not try active regen anymore unless soot drops to normal level
  3. Ash Accumulation Ratio ≥ 100% (also sets DTC Ash Overfill). Need to clean or replace DPF and tell the ECU to reset this value.
  4. DPF Limp-Home Mode (also sets DTC DPF Limp-Home Mode).

DPF Light Control

At ignition ON, the DPF light illuminates for two seconds exactly – light test –  and should stay off afterwards, indicating normal condition. Otherwise see above.

On Impreza Euro 4 / 5, ECU operates DPF light using CAN message 0x600 byte 0 bit 1, bit = 1 means light ON.

Euro 6 just seems to use different CAN IDs and frame contents.

Like we did in above GIF animation, flash rate is 400 ms/400 ms on/off (8/8 CAN messages at 50 ms interval), respectively. There are no other stock flash modes. In the future we’d like to use DPF light for additional purposes e.g. indicating active regeneration by implementing a different flash rate. We already did a patch using DPF light to indicate active regeneration: Diesel ECU Patch v1. For source code see post DPF Light Patch.

Updates

  • 2016-10 incorporate Euro 6 findings