jump to navigation

Build your own 2JZDuino v0.2 2012/12/12

Posted by Michael in 2JZduino.
add a comment

For anybody interested in building their own 2JZDuino shield or even using the design as reference for your own creation, here are some technical details on the hardware.

  1. Eagle .brd (board) and .sch (schematic) files can be downloaded here – use your favorite PCB fab shop and follow their instructions to convert these into circuit boards. I used http://dorkbotpdx.org/wiki/pcb_order/
  2. Reference my Bill Of Materials for populating the components.

I have not to-date documented the schematic for the LCD display very well, but if you search around the Arduino community there is plenty of information there on how it’s done.


Crankshaft Pulley Removal 2012/10/06

Posted by Michael in my IS300.
add a comment

There is a long thread at my.is about timing belt and water pump service for IS300s, with a lot of people encountering difficulty removing the crankshaft pulley bolt…


I changed my timing belt  yesterday and hacked up a custom tool to hold the crankshaft pulley in place while the bolt was loosened.  In addition to using this tool, I left the vehicle in 5th gear with the parking break pulled.  Once I started pulling on the breaker bar the bolt was loose within about 2 minutes.  See pictures below…

Key dimensions of the crankshaft pulley…

My custom tool, made from scraps of aluminum and the remainder of a satellite dish mount.  On the bolts you can see aluminum spacers I used because the bolts I had were too long.  The bolts only need to be long enough to extend 30mm past the front face of the pulley.  Note that if your bolts are much longer they will interfere with the timing belt cover that’s behind the crankshaft pulley.

Here the tool is installed on the crankshaft pulley and supported by a jack-stand (to oppose the torque from the breaker bar).

While pulling on my 3′ breaker bar the front of the car lifted a few inches.  After rocking on it a few times it cracked loose.

Lastly, a regular harmonic balancer puller made removal of the pulley from the crankshaft straightforward.

2JZDuino 0.5 Beta (public release), and Arduino EEPROM 2012/07/16

Posted by Michael in 2JZduino.
1 comment so far

I’ve posted a minor update to 2JZDuino. Get it from the 2JZDuino project page at SourceForge .
Download “2JZDuino 0.5 beta.zip”.

Included in the .zip package is the project folder (including source-code) for the Windows EEPROM programmer I wrote to support 2JZDuino; IS300_Arduino_EEPROM.

The only changes I implemented since the 0.4.x release are some adjustments to target a more stable idle and acceleration. For a long time I’ve had an issue where MAF turbulence caused the injector pulse-widths to vary significantly at idle. I implemented some logic that intervenes at idle if the injector signal leans-out too quickly.

The acceleration change was simply based on the volumetric efficiency (VE) I’ve estimated for my engine. Minimum injector Pulse-Width enforced during tip-in is now calculated for a higher assumed VE.

0.4.1 Alpha (4th public release) 2011/08/17

Posted by Michael in 2JZduino.

I’ve posted another release of 2JZDuino at Sourceforge, available here

This release comes after an extensive amount of testing and a few minor code changes & tweaks. As of this release I’ve logged about 10,000 km operating my IS300 with the 2JZduino connected as a piggyback ECU. For a large part of it I’d been fighting with a lean AFR condition that seems to occur either at low RPM when the throttle is partially depressed (i.e. tip-in), or at idle. I’ve speculated that there is an issue with the stability of the MAF reading due either to the larger intake tube diameter I have installed (3.5″ diam. vs. 2.75″ stock diam.), or turbulence around the MAF sensor due to the shortened intake tract.

There are two symptoms…

1) At idle the AFR would sometimes randomly drift lean to ~17:1 or rich to ~11:1 before the short-term fuel trims would correct.

2) For low RPMs and partial throttle the AFR would drift lean to upwards of 18:1. This was often observed when accelerating gently (MAP readings ~80kPa) from idle RPMs. Under this condition the ECU seems to stay in closed-loop operation which doesn’t provide enough fuel enrichment when the throttle first begins to open. The result is a significant loss of torque when trying to accelerate from low RPM. More throttle would trigger open-loop behaviour and the ECU would command the correct fuel for ~13:1 AFR. But part-throttle often resulted in a fuel shortage until the O2 sensors would command the short-term fuel trims to recover (or driver throttle input triggered closed-loop condition).

This highlighted a potential risk for when the Supercharger is installed… part throttle could result in positive manifold pressures which when mixed with lean AFR conditions could result in engine damage.

For both problems I implemented some logic in the h_InjectorChange() function that intervenes with the injector pulse width if a lean AFR condition is detected. The code is as follows…

  if (!InjectorEventState[Inj_Index]) { // only intervene with InjOFF events
    unsigned int thisInjPWcmd_T64 = TCNT4 - T64_LastInjON[Inj_Index]; // calculate the commanded PW for this Injector and for this combustion cycle
    if ((Inj_Index < 3 && AirFuelRatioB1_x10 > 160) || (Inj_Index >= 3 && AirFuelRatioB2_x10 > 160)) { // this Injector # is part of a bank that is currently LEAN
      // Sometimes at tip-in/accel. from idle a lean condition occurs (for MAP >~60kPa) -> Add fuel to obtain the expected amount
      if (!EngineIsStarting && ManifoldAirPressure > 60 && thisInjPWcmd_T64 < ExpectedIdleTipInInjPW_T64[MAPindex]) EventDelay += ExpectedIdleTipInInjPW_T64[MAPindex] - thisInjPWcmd_T64;
      // Sometimes AFR drifts lean at steady-idle -> Add 1/2 difference in fuel between previous injection dwell and Normal Idle fuel amount
      else if (ManifoldAirPressure >= 27 && EngineHz < 22 && thisInjPWcmd_T64 < NormalIdleInjPW_T64) EventDelay += (NormalIdleInjPW_T64 - thisInjPWcmd_T64) >> 1;

Effectively, if the AFR is ever detected to be leaner than 16:1, the logic will intervene and add to the injector pulse width: an amount to drive the AFR to stoichiometric AFR for MAP > 60 kPa, and half the amount to drive AFR to stoichiometric for the idle condition. The stoichiometric amount is pre-calculated based on an assumed volumetric efficiency of 50% (a reasonable estimate for the situation where these lean conditions tend to occur).

The logic is not intended to enrich the fuel past stoichiometric because the stock ECU fuel control still needs an opportunity to work. The idea is simply to enrich the fuel mixture temporarily while the stock closed-loop system responds.

For the idle condition the enrichment is only half of the difference between the expected and actual pulse-width (allowing the stock closed-loop control more of an opportunity to intervene). Generally the idle AFR fluctuations are more slow-moving, while the part-throttle lean condition is a fleeting condition requiring more aggressive intervention.

There is no intervention for MAP < 27 kPa, which is the point where the engine is deemed to be in vacuum and is decelerating (i.e. driver throttle input is zero).

Other changes since the v0.3 release…

  • Volumetric Efficiency considerations added to the calculation of InjectorOffsetsT64[][]. The volumetric efficiency for each point on the RPM vs. MAP table is now considered when the stoichiometric fuel amount is estimated (refer to this post). This should provide a more accurate fuel adjustment from the Injector Scaling map page of IS300_Arduino_EEPROM.exe.
  • New logic added that tracks EngineIsStarting. When the engine first starts to crank a maximum injector pulse-width is imposed to prevent over-enrichment in the case where larger injectors are installed. The stock ECU commands a lot of fuel during engine cranking. When test fitting my 440cc/min injectors I found this to result in much too much fuel during engine start. IS300_Arduino_EEPROM.exe now allows you to set the “Max Cranking AFR” which calculates a maximum pulse-width for 100% VE for the “Fuel Injector Size” value.
  • Logic implemented to intervene with lean conditions at idle, and under part-throttle (per the elaborate description above).
  • I’ve had some limited time to test the operation of 2JZduino with larger fuel injectors. I was limited by what seems to have been a small air leak between the injectors and the intake manifold, leading the engine to behave like there was a vacuum leak at low RPM. I’ve run into delay after delay in resolving this mechanical issue, so further testing will have to wait for now.

Volumetric Efficiency 2011/04/15

Posted by Michael in 2JZduino.

Early on in developing 2JZduino I made an assumption that the Injector Pulse length for each combustion cycle was exclusively a function of Manifold Pressure. The reasoning was that independent of all other variables the volumetric efficiency of the air intake system would remain mostly constant and a certain Manifold Pressure correlates strongly to the volume of air in the combustion chamber.

This is grossly incorrect. In hindsight it’s obvious that the engine replaces air at varying efficiencies (which explains things like why there is such a strong torque curve in a 4-stroke engine), but I was mistaken in the magnitude of the effect. And being so large, the effect of VE has a significant impact on fuel trims (which were previously assuming VE ~= 90%), upwards of 400% under some engine conditions.

To go about resolving this I logged a fair amount of engine data intentionally exercising a range of engine speeds and manifold air pressures so that I could calculate volumetric efficiency in post processing. The datalogger object in the 2JZduino code measures Engine Hz, MAP, AFR (banks 1 & 2), and Injector Pulse Length every 167ms. With this information I have measurements for the amount of air in the combustion chamber (calculated from AFR and Injected Fuel amount), and quantity of air for 100% efficiency (manifold pressure). The actual volumetric efficiency for each data-point is then calculated as the ratio of the two. The calculation looks like this…

2JZduino datalogger Measurements:
Hz = instantaneous engine speed
MAP = instantaneous manifold air (absolute) pressure, kPa
AFR = Air Fuel Ratio (latent)
T_inj = injector pulse length for previous combustion cycle, ms

Constants and Knowns:
Q_inj = 3.32 g/s = fuel injector flow rate
Inj_Latency = 1 ms = opening time of the injector solenoid
V_cyl = 0.5L = cylinder volume (swept)
CR = 10.5 = engine compression ratio
d_Air = 1.25 g/L = approximate air density
P_atm = 101 kPa = atmospheric pressure

Solving for the combustion chamber air mass that corresponds to 100% volumetric efficiency:
V_chamber = 0.5 + 0.5/(10.5-1) = 0.553L = total volume of combustion chamber
M_air_100% = 0.553 * d_Air = 691mg = air mass for 100% VE and MAP = P_atm

Solving for the instantaneous air mass in the combustion chamber for the instantaneous measurements: e.g. for Hz = 38, MAP = 66 (absolute, T_inj = 5.0, AFR = 14.1
M_fuel = (T_inj – Inj_Latency) * Q_inj = 13.3 mg
M_air = M_fuel * AFR = 186.5 mg

Solving for measured VE:
VE = M_air / (M_air_100% * MAP/P_atm) = 186.5 / (691 * 66/101) = 41.3%

Using these calculations for every logged data point I was then able in post processing to begin separating/sorting/analyzing the relationship between engine speed, manifold pressure, and volumetric efficiency. I eventually arrived at the following formula empirically that calculates volumetric efficiency (percent) as a function of engine speed and manifold pressure…

Eq. #1)
VE(Hz, MAP) = 12 + (Hz * MAP)/320 + MAP/2.67 – Hz/16 + 100/Hz
…constrained as VE > 20% and VE > 90%

This relationship was then used to calculate and predict (rather than measure) the volumetric efficiency for each data point in the datalog, and then further predict the pulse length that would be required for the measured AFR. Finally the error is calculated between actual injector pulse length and predicted injector pulse length. Continuing from the example above the calculation is as follows…
VE(38, 66) = 44.8%
M’_fuel = (V_chamber * d_Air * MAP/P_atm)/AFR * VE(Hz, MAP) = 14.4 mg
T’_inj = Inj_Latency + M’_fuel/Q_inj = 5.3 ms
Pulse Length Error = 5.3 – 5.0 = 0.3 ms

As verification to the empirical VE formula, the graph below shows select portions of the data-logs that present the measured and calculated (using Eq. #1) injector pulse lengths alongside the error between them. For reference the corresponding engine MAP is also shown.

Clearly, the errors between calculated and measured injector pulse lengths are held mostly to less than 1ms. Exceptions to this occur primarily under conditions where injector pulse-lengths change rapidly (caused by rapid changes in throttle position). In these instances the response times of sensors and even the 2ZJduino are the likely sources of error.

This particular calculation for VE (Eq. #1) shows to be a rather good approximation. Now with an understood of volumetric efficiency for my particular 2JZ-GE, next steps are to apply the calculated VE values to the fuel trim map in 2JZduino. More on this in a future post.

For reference, below is a map of measured Volumetric Efficiency of my 2JZduino for Engine Hz vs. Manifold Air Pressure…

UnoEngineSim and (hacked) Interrupt Priorities on Arduino Mega 2011/04/05

Posted by Michael in 2JZduino.
add a comment

During testing I began to experience a misfire that was only occurring above about 5500 RPM. I wondered if the igniter signals were sometimes being delayed due to over-lapping interrupts that were higher priority. I’d recently purchased an Uno as a second Arduino and so I wrote an engine simulator that would run on the Uno to test my theory.

The Uno was configured to produce Injector and Igniter output signals at the intervals typical of a 2JZ-GE engine running at about 6000RPM. These outputs were connected to the inputs of 2JZduino. One-at-a-time the signals *output* by 2JZduino were connected back into the Uno on Pin8; the Input Capture pin, so that the latency of 2JZduino could be measured. The Uno would then report out statistics on the time from when the simulated output signal was generated, to the time that 2JZduino recreated the signal (captured by the ATmega 168’s Timer1 Input Capture register).

Below is a copy of the EngineSim code run on the Uno. Once a second, it reports out the # of events, minimum/maximum/average event delays, and the number of events that fell below the average (providing an estimate of the spread of the data).

// Pin assignments
// Pin2(PD2) = Inj1sim
// Pin3(PD3) = Inj2sim
// Pin4(PD4) = Inj3sim
// Pin5(PD5) = Inj4sim
// Pin6(PD6) = Inj5sim
// Pin7(PD7) = Inj6sim

// Pin8(PB0) = Input Capture

// Pin9(PB1) = IGT1sim
// Pin10(PB2) = IGT2sim
// Pin11(PB3) = IGT3sim

// ********************
// Establish all timing for engine speed of ~100Hz = 6000RPM (28us / deg)
// ********************

volatile unsigned int tEventQueue; // time the event was queued
volatile unsigned int tEventDelayMax; // max elapsed time from Event-queued to Input-Capture
volatile unsigned int tEventDelayMin; // min elapsed time from Event-queued to Input-Capture
volatile unsigned long tEventDelaySum;
volatile unsigned int tEventCount;
volatile unsigned int tEventCountSmall;
volatile unsigned int tEventDelayAverage;

void MonitorInput()
  TCCR1B |= B10000000; // Noise Canceler = ON, Capture Falling Edges
  TIMSK1 |= B00100000; // enable Input Capture interrupt
  DDRB &= B11111110; // B0 is an input
  PORTB |= B00000001; // B0 pull-up resistor ON

void StartIGTandInjSim()
  DDRB |= B00001110; // IGT1..3 sim on PB1..3
  PORTB &= B11110001; // Ensure IGT1..3 signals are OFF
  DDRD |= B11111100; // Inj1..6 sim on PD2..7
  PORTD |= B11111100; // turn on Inj1..6sim -> signal is active low
  TCCR1A = 0; // Normal operating mode
  TCCR1B = B00000010; // clk/8 -> 32.77ms roll-over
  TIMSK1 |= B00000110; // enable OCIE1B and OCIE1A 

void StopIGTandInjSim()
  PORTB &= B11110001; // Ensure IGT1..3 signals are OFF
  PORTD |= B11111100; // turn on Inj1..6sim -> signal is active low
  TIMSK1 = 0;

ISR(TIMER1_COMPA_vect) // IGT sim
  // Toggle Sequence: 1,1,2,2,3,3
  const byte IGTbitmaskSeq[6] = { B00000010, B00000010, B00000100, B00000100, B00001000, B00001000 };
  static byte IDX = 0;

  if (IDX == 1) tEventQueue = TCNT1; // Queue the event for IGT1 ON
  PINB = IGTbitmaskSeq[IDX]; // toggle in sequence
  if (IDX >= 6) IDX = 0;
  OCR1A += 5247; // Next event: 3250/65536 * 32.77ms = 1.62ms -> 3 IGT pulses (equally spaced) each revolution

ISR(TIMER1_COMPB_vect) // INJ sim
  PIND = B11111100; // toggle all the injectors simultaneously
  OCR1B += 10003; // Next event: 10000/65536 * 32.77ms = 5ms -> Sim. all Injectors pulse each revolution

  unsigned int Delay = ICR1 - tEventQueue;
  if (Delay > tEventDelayMax) tEventDelayMax = Delay; // store the peak delay that occurred
  if (Delay < tEventDelayMin) tEventDelayMin = Delay; // store minimum delay that occurs
  if (Delay < tEventDelayAverage) tEventCountSmall++;
  tEventDelaySum += Delay;

void StartSim()

void StopSim()
  tEventDelayMax = 0;

void setup() {
  Serial.println("IGT1 ON Event Delay in clk/8 counts...");


void loop() {
  if (Serial.available())
    char c = Serial.read();
    if (c == 's') {
    else if (c == 'x') {
  if (tEventDelayMax > 0) 
    Serial.print(tEventDelayMin, DEC);
    Serial.print(" .. ");
    Serial.print(tEventDelayMax, DEC);
    Serial.print(" (");
    Serial.print(tEventDelayAverage, DEC);
    Serial.print(tEventCount, DEC);
    Serial.print(tEventCountSmall, DEC);
  else Serial.print(".");
  tEventDelayAverage = tEventDelaySum/tEventCount;
  tEventDelayMax = 0;
  tEventDelayMin = 65535;
  tEventDelaySum = 0;
  tEventCount = 0;
  tEventCountSmall = 0;

Note that the Injector and IGT signals are intentionally setup on different frequencies so they drift in and out of phase in over-lap. This was done to ensure a worst-case scenario would eventually surface where the injector events, ADC_complete events, timer overflows, etc. would all interfere with the IGT events. What I found is that on some occasions the igniter signals would be delayed by upwards of 600us. At 5500 RPM this translates to about 20 degrees. The Uno simulator was written as worst-case (with all 6 injectors firing simultaneously), but it was at least seeming plausible that under some circumstances interrupt conflicts could cause the igniter signal to be delayed long enough that a misfire could occur because the engine spark arrived too late.

Looking at the datasheet for the Atmega 1280, the Interrupt vectors show that the IGT interrupt requests (Pin Change Interrupt Requests 0/1/2) are lower in priority than the Injector interrupt requests (External Interrupt Request 0/1/2/3/4/5). What I wanted was for the IGT interrupts to be treated with higher priority than the Injector interrupts. Spark events are critical in timing, but getting slightly more or less fuel would have a negligible effect. The solution I arrived at was to configure the interrupt handlers for the Injector External Interrupts like this…

ISR(INT0_vect, ISR_NOBLOCK) { // Ext Interrupt 0 on D0

In this way with the “ISR_NOBLOCK” argument, if all 6 Injector interrupts and an IGT interrupt occurred simultaneously the external interrupts would all queue up on the stack, each one interrupting the other, until the IGT interrupt gets serviced as the highest priority interrupt. The IGT interrupt, being a blocking interrupt, would be fully-serviced, and then the Injector interrupts would start getting popped off the stock one-by-one, unwinding until normal program flow continued.

With this change the maximum delay on the IGT signal was reduced from 600us down to 40us. 95% of the time the IGT signal delay was < 20us. This translates to better than 1deg of latency in the IGT signal at 6000 RPM under worst-case conditions.

It turned out that the real-problem causing the high-RPM misfire was due to a 220 Ohm resistor being installed where there should have been a 10k Ohm resistor. But the above was a good improvement nonetheless.

0.3 Alpha (3rd public release) 2011/04/05

Posted by Michael in 2JZduino.

I’m over-due for another release. As mentioned in my post 0.2 Alpha (2nd public release). I’ve been working on replacing the crank sensor intercept with IGT signal intercept. This is now working successfully and is implemented in v0.3 available here…
2JZduino at SourceForge

Here’s a list of significant features new to v0.3…

  • Developer notes are now found in “DevNotes.txt” (they used to be at the top of “IS300_MAPadd.cpp”).
  • Note the inclusion of “IS300_Arduino_EEPROM.exe” in the sourceforge download. This program will talk to and program the EEPROM values into 2JZduino.
  • Igniter control – the code no longer supports interception and reproduction of the crank signal. Instead it intercepts and reproduces the igniter signals: IGT1, IGT2, IGT3. “IGT ON” events are always executed immediately (which charges the igniter circuit). “IGT OFF” events (which marks the firing of the spark) are executed depending on TimingRetardTable[][] values.
  • Fuel Injector pulse-adjustments are now calculated based on the Fuel Injector size stored in EEPROM.
  • Simulated narrowband circuit ground is now connected through PortB0 (Arduino pin 53). The code floats this pin as an input until the LC-1s show they are ready. As an input, the high-impedance drives the stock Toyota ECU into open-loop mode until the LC-1 begins outputting a meaningful signal. When it switches to an input (held low), the ground patch triggers the stock ECU to switch into closed-loop.
  • Improved interrupt efficiency. Igniter interrupts are now always serviced within 40us of their occurrence (20us 95% of the time). Injector interrupts are serviced within 60us of their occurrence.
  • Engine speed now calculated from IGTon signals instead of Crank sensor signals.
  • Datalogger expanded to now include RPM, MAP, Bank1 AFR, Bank2 AFR, and Injector pulse-width data. Datalogger information is now saved in a .csv file by “IS300_Arduino_EEPROM.exe” using the current date/time in the filename.
  • Reduced amount of SRAM used at run-time.
  • Added advanced injector and MAF compensation logic to EEPROM that will scale fuel injectors based on the new vs. stock fuel injector size, new vs. stock MAF sensor intake tube diameter, fuel injector lag, and a DC bias for the MAF signal. *Note: this is experimental and unverified. More details on this in a future post.

Additionally, here’s a summary of the verification testing that I’ve completed since the last release. I’ll be posting some more analytical information on these in the near-term, but for now, here are the results.

  • My 2nd LC-1 wideband has been installed. I’ve now gathered more than 20hours of operation with 2JZduino providing the simulated Narrowband signal for closed-loop fuel control. It’s been successfully fooling the stock ECU into adjusting for an AFR of 15.1 instead of 14.7 while under closed-loop operation.
  • ~4 hours of operation with 2JZduino providing active Injector Scaling (providing leaner fuel injection amounts at wide-open-throttle).
  • Verified that all 10k resistors are indeed 10k resistors… one of the resistors in the circuit for Injector #4 was found to actually be a 220 Ohm resistor. This culprit was understandably causing me all kinds misfire problems for awhile. I’ve learned a good lesson here :)

2ZJDuino v0.1 Shield – Finished Product 2011/01/30

Posted by Michael in 2JZduino.
add a comment

After finally receiving all the parts I’ve found the time to finish building the packaged 2JZDuino v0.1. I recently posted about the custom PCB and packaging design. Below are photos of the actual unit built to that design.


  • 1/4″ pressure line for MAP sensor
  • Mini-Fit Jr. Molex connectors for engine bay connections; LCD, ECU connections, engine connections, power, Wideband sensors, simulated Narrowband signal
  • External ICSP connector for reprogramming
  • Onboard temperature sensor
  • Enclosed in a Bud Industries PI-1908, dimensions: 5.9″ x 3.2″ x 2.4″ (15% smaller than the prototype unit)

  • 2JZduino v0.1 PCBs 2011/01/05

    Posted by Michael in 2JZduino.
    add a comment

    The PCBs made through DorkbotPDX’s PCB Order arrived in the mail today. I’m quite pleased with the quality of the boards. Not that I have that much experience with PCB fabrication, but these exceeded my expectations, and Laen’s prototyping service seems to be the best value ($5/square inch and you get 3 copies). Photo below…

    Raptor V and Intercooler Installation 2011/01/01

    Posted by Michael in my IS300.
    add a comment

    There was a lot of iterative fitting, measuring and fabricating, but I’ve finally got the Raptor V mounting brackets and the intercooler piping done. Pictures tell this story best…

    The Raptor mounts to a generic plate supplied by 928 Motorsports. I custom-built two pieces to attach to the side of the 2JZ engine via two existing bosses; one pre-tapped for an M10, and the other pre-drilled for an M10. Also visible in some photos is the 3″ hole drilled through the body sheet-metal and lined with rubber hose for the intercooler piping.

    A third custom piece attaches the Raptor mount plate to the alternator bolt. These components are all sized to align the pulley for the serpentine belt.

    The inter-cooler is mounted to the crash-bar with M8 bolts and steel right-angle brackets.

    The plastic liner for the front-left wheel-well gets a big hole for the inter-cooler piping pass-thru.

    With the plastic belly-panel removed. looking from the middle of the car at the hole in the plastic liner.

    Now with the inter-cooler plumbing installed…

    I fabricated an aluminum heat-shield to fit between the intake filter and the exhaust manifold. Also visible is the aluminum air manifold (with copper and brass fittings). The manifold collects exhausts from the PCV, cooling air for the Raptor, and the Bosch bypass valve, and redirects it back into the compressor intake.