Hacking my WRX with Python (ECU Tuning, Part 1)

Classic Disclaimer: I don’t know what I am talking about when it comes to tuning your car. I am not a mechanic, pro tuner, or automotive engineer! Anything listed here is for educational use only. I’d highly advise you to take your vehicle to a professional tuner if you are considering modifying it, and not to DIY.

When I was 13, I saw The Fast and The Furious and it was life changing. (Readers of the blog will know that I have a soft spot for bad late 90s movies). I dreamed of one day being able to save up enough to buy a JDM tuner car like in the movie, because the idea that someone could just build a race car was an absolute revelation. After I got my license, my parents bought me a 1988 Mazda MX6. It didn’t have a turbo, but it was manual. Tokyo Drift had just come out, and it took me roughly 5 weeks to completely ruin the clutch on that car (granny shifting when I should have been double clutching). It took another 5 weeks to save up enough to pay to replace the clutch, and that was the end of my street racing career. Then the recession happened, and cash for clunkers, and the dream of owning a car that I’d be brave enough to modify went out the window.

Then covid happened. I’d been ‘stranded’ in Florida and driving a borrowed Hyundai for a few months when I decided to just buy a car. My partner owns a ‘Blobeye’ WRX that has lived through three engines (Her newest being a JDM EJ207). It sounds like a pack of Harleys, handles like a Hawker Hurricane and can bring a Dodge Challenger to gapplebees at the quarter mile. Subaru had just fitted the new generation of WRX with a new direct injection engine, so I picked one up. Enter “Tohru”.

One of the first ‘mods’ most people do to the WRX is installing a device called an AccessPort. The AccessPort (by Cobb Tuning) plugs into the car’s OBD2 port and allows you to interface with all of the readings therein. It also allows you to flash custom “maps” to your ECU’s EEPROM. I went ahead and voided the fuck out of my warranty and flashed Cobb’s “Stage 1 Off-The-Shelf” tune onto my car’s ECU.

The stock 2015+ WRX is tuned to pass emissions and, presumably, reduce wear on the parts of a new car. I also suspect that the torque curve is a bit aggressive as to give test drivers a taste of the turbo boxer engine’s power even at 20% throttle, but let me tell you, I really didn’t love driving it. The aggressive torque curve and ignition maps made for a fairly clunky ride when being sensible, and a fairly disappointing power increase when you stepped on it. The Cobb Stage 1 map resolved that entirely, and made the car a dream to drive (Also supposedly gives you a double digit horsepower increase.)

I was obsessed. How could a simple rom file have that much impact on my car’s performance? I had to learn how to tune this thing. Only, Cobb paygates (well, cert-gates) their tuning software for AccessPort. Fair enough, you wouldn’t want some unqualified hacker tinkering themselves into blowing an engine. I went ahead and got the tuning cert, I was just too curious to see how things worked on a software level.

In short, a whole lot more than I expected. It operates kinda like a database, with a number of 2d hash tables. Sensor and user input is referenced by these tables and an output is generated. Generally the things you are tweaking while tuning will fall into the category of Fuel, Ignition and Boost as well as calibrating sensors (to affect the aforementioned 3 elements.)

Some tables are read-only, while others are updated in RAM. This allows the ECU to ‘learn’ and compensate for certain elements over time. If that sounds kinda like machine learning, that’s because it totally is machine learning! I got to experience an example of this learning while on a road trip. My car was tuned for 93 octane, but the gas station I stopped at only had 92. After driving a bit, I noticed that my “Feedback Knock” sensor had seemingly detected early signs of engine knock, and at given RPM ranges automatically began retarding my ignition timing to compensate. After I refueled with 93, the timing changes were still there, but over the course of an hour or so, the ECU began to gradually advance the timings at that given range because it was no longer detecting knock. While I wouldn’t want to depend on this failsafe, it amazing to watch my car TUNE ITSELF in real time.

I was surprised to find that the process of tuning is not too dissimilar to overclocking. You increase certain variables (voltage for overclocking, timings/etc for tuning) until you reach a point where problems occur (overheating for CPUs, engine knock for cars). The difference is that there are a lot more variables, and well, while frying a CPU is expensive, throwing a conrod through your engine is both more dangerous and more expensive!

For the AccessPort, Cobb provides ‘Off the Shelf’ maps as a safe template to begin with. These maps exist to allow you to have a ‘good enough’ tune for the time between modifying your car and having it tuned, as well as a starting place for a tuner. These maps vary depending on which modifications, if any, have been done to the car. I am using their Stage 1+ 93 map, which is for vehicles with upgraded air intakes.

Something I regularly see (pro) tuners suggest is starting with calibrating your Mass Airflow Sensor. The MAF provides the ECU with a reading of how much air (grams per second) is going into the vehicle’s intake. The logic behind tuning this sensor first is that your mass airflow sensor generates information that is used by a good portion of your ECU maps and tuning any of those maps without first having an accurate idea of your car’s load will result in inaccurate tunes. Having even a 10% inaccurate maf sensor will affect your car’s performance noticeably. When I installed my cold air intake, I relocated my MAF sensor which would certainly change the accuracy of the readings.

But before any of this, we will need to LOG.

You can gather logs from your OBD2 reader, in my case the AccessPort. These are stored as simple time-series .csv files with various columns. Here’s an example of one I took while calibrating my MAF sensor.

datalog from cobb accessport

I found that reading this into python as a list of dictionaries was most useful. That looks something like this:

import csv

log_file = csv.DictReader(open("datalog.csv"))
datalog = []
for row in log_file:
datalog.append(row)

The sensor itself produces a 0.0 to 5.0 volt signal, which is referenced by an ECU map (Mass Air Flow Sensor Calibration), which maps voltage readings to Grams/Second numbers. These numbers are used for calculating “Calculated Load”, which affects ignition timings, boost numbers and torque numbers. Calculated Load is measured in Grams per Revolution, and is simply (MAF * 60) / RPM.

I wasn’t able to find specific details on how to best calibrate this sensor, but through trial, error, and browsing old forum posts, I think I have a rough idea of how to do it.

The columns that we are most concerned with are AF Learning 1 (%) and AF Correction 1. AF Correction is how much fuel is being added to correct for inaccurate airflow measurements, AF Learning refers to a learning table based on historical AF Correction data. Adding these together gives you a rough percentage of how inaccurate your airflow readings are. We will also want to reference the MAF Voltage column, to determine at which voltage we have inaccuracies.

airflow_corrections = {}for line in datalog:
total_correction = float(line["AF Learning 1 (%)"]) + float(line["AF Correction 1(%)"])
maf_volts = line["MAF Voltage"] if maf_volts not in airflow_corrections:
airflow_corrections[maf_volts] = []
airflow_corrections[maf_volts].append(total_correction)# average each MAF voltage correction list
for voltage in airflow_corrections.keys():
avg_corr = sum(airflow_corrections[voltage]) / len(airflow_corrections[voltage])
airflow_corrections[voltage] = avg_corr

From here, we are given a dictionary of voltages and a corresponding average correction percentage. This can then be use in Cobb AccessTuner to modify the Mass Airflow Calibration table, multiplying the grams/second values by the percentage indicated at the voltage level. I also ended up only using readings that were actually column keys, but this is something you can play with if you decide to try this.

From here, we take our modified map, save it to the tuner and reflash our ECU with the new readings. I drove around a bit before logging, to let the AF Learning populate once again. Ideally we are looking for less correction this time. I iterated on this 3 times before I was satisfied with the correction level (Ideally you’ll want less than 10% +/- across the board for your AF Learning levels under regular conditions.)

Surprisingly, just properly calibrating the airflow sensor led to my car actually feeling more powerful, because in my case it was giving a reading that was lower than the actual amount of air entering the engine by 15% in some places. That means that properly calibrating it has allowed my vehicle to correctly calculate load, and in turn, give me the correct amount of fuel and “requested torque”.

Time to race Evos for pinks.

Stay tuned for part 2, where we overnight parts from Japan to build the 10 second car that we owe Vin Diesel.

“Defense Researcher” according to Reuters, Chelsea Manning Fan Fiction Author, Delightful Degenerate