Tag Archives: Euro5

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-2016)	https://subdiesel.wordpress.com
// c++14

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

using namespace std::chrono;

/**
 * @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)
 *
 */
constexpr auto interval {50ms};
/**
 * @brief stock period for flashing mode is 800 ms,
 *        does not have to match stock here
 *
 */
constexpr auto dpfLightPeriod {800ms};
/**
 * @brief defines DPF light output over time when active regeneration is on
 *
 */
constexpr std::array<bool, dpfLightPeriod / interval> dpfLightCustomPattern
{   {   1, 1, 0, 0,  1, 1, 0, 0,
        0, 0, 0, 0,  0, 0, 0, 0
    }
};

/**
 * @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 (c) 2011 SubaruDieselCrew

/*
	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);
…


Disassembly using objdump which is part of GNU binutils. Binary had been generated by GCC. I added quite verbose comments for those who don’t know SH (SuperH) disassembly well enough.


void calc_DPFLight_continue()
  c: 91 1b  mov.w   0x46,r1 ! 9c1f   // r1 = &DPFLightModeEnum_b = 0xFFFF9C1F (sign extension of value 0x9c1f)
  e: 60 10  mov.b   @r1,r0           // r0 = DPFLightModeEnum_b
 10: 88 01  cmp/eq  #1,r0            // DPFLightModeEnum_b == ONsteady ?
 12: 8f 02  bf.s    0x1a             // if not --> jump
 14: 71 ff  add     #-1,r1           // r1 = 0xFFFF9C1E = &DPFLight_bool
 16: 00 0b  rts                      // return (after following instruction)
 18: 21 00  mov.b   r0,@r1           // DPFLight_bool = true

 1a: 92 15  mov.w   0x48,r2 ! b222   // r2 = &DPF_Regeneration_bool_SSM = 0xFFFFB222
 1c: 62 20  mov.b   @r2,r2           // r2 = DPF_Regeneration_bool_SSM
 1e: 63 2c  extu.b  r2,r3            // r3 = (uint8)DPF_Regeneration_bool_SSM
 20: 23 38  tst     r3,r3            // DPF_Regeneration_bool_SSM == false ?
 22: 8d 0e  bt.s    0x42             // is false --> DPFLight_bool = false; return
 24: 00 09  nop
 26: 91 10  mov.w   0x4a,r1 ! 9c53   // r1 = &DPFLightCounter_b = 0xFFFF9C53
 28: e2 0e  mov     #14,r2           // r2 = CounterMax - 2 = 14
 2a: 60 10  mov.b   @r1,r0           // r0 = DPFLightCounter_b
 2c: 60 0c  extu.b  r0,r0            // r0 = counter = (uint8)DPFLightCounter_b
 2e: 30 26  cmp/hi  r2,r0            // counter > 14 ?
 30: 8d 02  bt.s    0x38             // if yes --> DPFLightCounter_b = 0
 32: 70 01  add     #1,r0            // ++counter
 34: a0 01  bra     0x3a             // --> DPFLightCounter_b = counter
 36: 60 0c  extu.b  r0,r0
 38: e0 00  mov     #0,r0            // r0 = 0
 3a: 21 00  mov.b   r0,@r1           // DPFLightCounter_b = r0
 3c: d1 04  mov.l   0x50,r1 ! 943c0  // r1 = &table[0]
 3e: 02 1c  mov.b   @(r0,r1),r2      // r2 = table[counter]
 40: 91 04  mov.w   0x4c,r1 ! 9c1e
 42: 00 0b  rts
 44: 21 20  mov.b   r2,@r1           // DPFLight_bool = r2; return
 46: 9c 1f                  // 0x9c1f
 48: b2 22                  // 0xb222
 4a: 9c 53                  // 0x9c53
 4c: 9c 1e                  // 0x9c1e
 4e: 00 09  nop
 50: 00 09                  // 0x943c0 --> blob position 3c0
 52: 43 c0

3c0: 01 01  .word 0x0101    // table[16] = { 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
3c2: 00 00  .word 0x0000
3c4: 01 01  .word 0x0101
3c6: 00 00  .word 0x0000
3c8: 00 00  .word 0x0000
3ca: 00 00  .word 0x0000
3cc: 00 00  .word 0x0000
3ce: 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). Same code should also work for all current Euro 4, 5 and 6 models.

Compiled binary generated from above source code is 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

  • 2016-11: updated source to C++14
  • 2016-10: updated source to C++11 with Doxygen documentation
  • 2016-01: added disassembly

Updated Extended OBD-II definitions

Added 7 PIDs (0x10A1, 0x10A3..0x10A7, 0x1137), some are petrol-only, some diesel-only, some mixed.
See Extended OBD-II definitions page.
Downloads (spreadsheet, CSV) as well as ParsePID project are up to date.
Note that some PIDs have not been tested on actual cars yet – feedback is appreciated!

Mode 23 – Read Memory

According to some of my notes, Euro 5 diesel models, or cars that support Extended/Enhanced OBD-II in general, might support mode/service 23 for dumping ROM or RAM blocks:

Format is similar or same as ReadMemoryByAddress (23 hex) service specified UDS (Unified Diagnostic Services, ISO 14229) protocol:

"23 <Format> <Address[]> <Length[]>"

Several $23 formats can be supported, Euro 5 diesel:

  1. "23 14 A1 A2 A3 A4 L1"
  2. "23 24 A1 A2 A3 A4 L1 L2"

As you can guess, format byte e.g. 0x14 means:

  • 4 address bytes → uint32 big endian, encoded in low nibble of format byte
  • 1 length byte → uint8, encoded in high nibble of format

Restrictions

Stock firmware usually restrict available address ranges, allowing only partial dumps. ROM calibration data and RAM regions might work. Knowing how to reflash and reverse-engineer the logic, such restrictions can be patched and therefore eliminated.

Errors

Depends on actual implementation, (early) Euro 5 diesel ROM logic:

  • 0 < length ≤ 0x400 (dec 1024) bytes otherwise NRC 31
  • other formats or request message lengths yield NRC 13
NRC (hex) Description
13 Incorrect message length or invalid format
31 Request out of range

Example

Euro 5 diesel (1.5 MiB ROM, SH7059 chip) example – dump beginning at ROM calibration data:

Start address = 0xC0000
Length (per request) = 0x40 = 64 bytes

"23 14 00 0C 00 00 40"
Positive response: "63 <XX XX XX ... total 64 payload bytes ... XX XX>"

"23 14 00 0C 00 40 40"

"23 14 00 0C 00 80 40"

"23 14 00 0C 00 C0 40"

"23 14 00 0C 01 00 40"

Anyone able to confirm that these mode 23 commands and formats are working?

Personal experience on many different control units: Maximizing length per request yields max transfer speed, however application algorithm must be able to handle NRC codes and react properly.

Definitions Download for Extended OBD-II

Uploaded first draft of definitions file for diagnostics protocol Extended/Enhanced OBD-II. Should be easy to work with, can export to CSV etc. Everyone is welcome to use the data as long as it is mentioned where it came from.
Also added a few more PIDs in the meantime.
Go to page: Extended OBD-II

Protocols page Extended OBD-II

Added 3 more DPF related PIDs on page Extended OBD-II:

114A Pressure Difference between DPF Inlet and Outlet
1155 Estimated Distance to Oil Change
1156 Running Distance since last DPF Regeneration

Will add some more over time…
Apparently newer petrol models now also use this protocol (exclusively apart OBD-II, no SSM2 anymore ?). Might add petrol specific PIDs in the future.
Also, apps like Torque have been confirmed working with this.
Anyone able to provide any data, testing new or known PIDs, screenshots of software including SSM etc. or spot any issues, please get in touch!

Updated protocols page Extended OBD-II

Updated protocols page Extended OBD-II:

  • corrected “Exhaust Gas Temperature (EGT) at DPF Inlet”
  • added “DPF Regeneration Count”

Euro 5/6 model owners please test and report results, thanks!

Would also be interesting to know (logging) software capable of using this protocol. Tactrix Openport 2.0 standalone logging, Torque perhaps???

ECU connection problem Euro5 v2

Update 2011-04-19: Apparently SSM via CAN does work on Euro5 while SSM via serial does not. Therefore Euro5-owners might need a CAN interface – more expensive, less software support, unfortunately. Not sure yet if serial diagnostics can be activated somehow or does not exist in ROM anymore. Euro4 supports both methods out of the box.

Apparently people cannot connect to Euro 5 Boxer Diesel ECUs using SSM software like RomRaider or FreeSSM.
Such applications use standard SSM2 protocol (4800 baud over serial K-Line, OBDII pin# 7).
All I can say for now is we’re starting to investigate, just takes time as our tech guys don’t own such new models.
Please let us know if any (non-OBDII protocol) software is working with your Euro 5 model.

Did anyone owning Openport 2.0 interface try standalone-logging via CAN method (logging to memory card) yet?

We could also use pics from OBDII port (below steering wheel) with metal pins visible for confirmation. OBDII port changes are unlikely, though, there should still be lots of other control units talking serial protocols.