Open source rail pressure tuning software for Diesel engines
C++ Arduino
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.
db2 schematic.PNG


Open source rail pressure tuning software for Diesel engines

Initial design is for the first and second generation Bosch diesel injection, tested on a VW.

Design process and tips at

Everything here is just a prototype and will not work out of the box. No warranty offered, it may even break your test car or trigger unresettable errors.


The module intercepts the high pressure rail sensor signal (0 to 5V) and modifies it before sending it to the ECU. Sending a lower value than the current one will cause the ECU to compensate and increase the rail pressure which in my case translates to decreased fuel consumption.

The Arduino (Nano, Pro Mini, anything ATMega328-based) is connected via the TX and RX lines to a HC-05 module. The bypass relay is controlled an ATMega output pin (7). When the relay is off, the signal from the sensor will pass unaltered to the ECU. When the relay is switched on, the signal passes through to the ADC input (A0), the ATMega computes a PWM value which is then sent to the output pin (9), low-pass-filtered, measured again (A1) and sent to the ECU.

version 2 schematic


The formula to determine the output value:

O = I * (C / 100) * (G/100) + (F-128) >> 2 where:

  • O is the 8-bit PWM output, currently running at ~60kHz
  • I is the 10-bit ADC input (A0)
  • C is one of the 15 point values that provides a multiplier. See below
  • G is a global gain, where 100 would mean no gain is applied and 120 means +20%. Used usually to calibrate the impedance
  • F is a global offset, centered at 128. Used usually to calibrate impedance but can also be used for tuning.

The C value is read from a 15-items array. The index is determined by the input value (I), minimum range and maximum range:

binSize = (maxRange - minRange) / 15; idx = (I - minRange - binSize/2 ) / binSize Where minRange and maxRange are 10-bit values. If the input value is between those two ranges, the C[idx] is read and the multiplier is applied using the formula above, otherwise the neutral multiplier is applied (100).

Serial output is separated (usually) by newlines. The starting character in the line determines the kind of data that is output:

  • '' - periodic status: printf("i:%d i2:%d o:%d p:%d en:%d\n", lastReadValue, lastReadValue2, lastWrittenValue, getCurvePoint(lastReadValue), moduleEnabled);
  • '%' - command output - from help, read setting, calibration, etc.
  • '>' - requested command output, where '>OK' signals success, everything else is an error
  • '{' - setting string, terminated by another '}'. See below for format
  • '<' - the main command (character) received via serial input Serial input must be separated by newlines. The goal of the serial protocol is to make it slightly human readable, take as little characters as possible and enable automatic processing.

Terminal commands:

  • h - help: show available commands
  • e - enable the module ('boost' car)
  • d - disable module
  • rm - reads the minimum value from which the module starts working
  • rM - reads the maximum range
  • rc - outputs the entire configuration (version, min, max, curve points)
  • sm - sets the minimum range. Example: input sm300 - press enter
  • sM - sets the maximum range. Example: sM800
  • sC - sets a point on the curve. E.g. sC5117 sets point five to value 117. This means that during that specific range the output will be offset above by 17%
  • sX - sets the entire configuration in a single string. Can be generated by the spreadsheet
  • S - saves the config to EEPROM; if a number (0-9) is specified after S a saving slot is used
  • L - loads the config from EEPROM, overwriting the current one; a number (0-9) can be appended after L to specify the loading slot
  • ~ - soft reset


By default the module starts disabled, outputing the unmodified ADC input. See blog post for details, the input and output impedance still need some work as the car expects very specific values.

There is the addition of a DEFAULT_ON switch which has the following behavior

  • at startup, if the switch is on (LOW) the module will switch to the enabled state
  • if the switch is off (HIGH or disconnected) the module will start in its default state, i.e. disabled
  • during runtime, if the switch is toggled the enabled state is also toggled. e.g. if module is off, switch is on (LOW), moving the switch to off enables the module


I am using a basic multitasking technique of time slicing. I could have used interrupt-based multitasking or some other fancier ways but then I have to watch out for race conditions, which is not an acceptable compromise in an automotive application. The high priority task runs every 5 miliseconds and performs the calculations above to determine the output. It also sends a few bytes from the serial buffer to the UART module, this number still has to be tuned.

The low priority task runs every second and provides a status update, reads serial commands and outputs serial responses.

Other resources

The .ods file in this repository is the spreadsheet used for visualising and generating the configuration command.