Arduino as MAF translator

Hey, I've got an interesting set up which should be pretty simple for someone who has a little know-how in this area. I'm just reaching out for help at this point because I don't know why I'm getting the results I'm getting. There's a short version at the bottom if you want to skip most of the reading.

The situation is that a friend of mine has a 97 BMW 328i we put a turbo on. There's no easy way to tune these cars, and piggy-back ECUs and FICs are in the $300-400 range... they don't do anything that this arduino I've had laying around can't do in one form or another so I figured I'd give this a shot before wasting a bunch of money.

We need to increase the voltage coming from the MAF by a percentage. Rough is fine till we can get it working, then we'll improve it. The MAF puts out 0-5 volts depending on how much air is flowing through it.

I want to intercept this 0-5v signal using the arduino, increase it, and send the modified voltage along it's intended path.

I know the arduino can't output anything but PWM, so I did some looking and started to get in above my head. I used a diagram to build a low pass filter, and it works! If I set the arduino to 100% duty cycle I get ~4.85 volts, and I get regular analog voltage across the entire range (even down to ~1 volt). I can't post a link to the guide I was using as I don't have enough posts yet. It suggested for the default PWM frequency of the arduino that 6.9k ohms of resistance and a 47 uF capacitor was ideal.

So I'm using a total of 6.85k ohms (measured) resistance, and a 47 uF capacitor for my low pass filter. The resistors are connected in-line on the signal, and the capacitor is connected between signal and ground, after the resistors. The low-pass filter seems to work - but it's output isn't linear.

I have the arduino read the analog voltage coming from the MAF (which it does fine), multiply it by 1.2, then divide by four, and send back out. The only issue is that when there's ~1.6 volts coming into the arduino, even after a 1.2x multiplication, ~1.3 volts comes out.

The response time of this filter seems to be fast enough. To be honest anything under a 100-150 ms time to reach new voltage is acceptable. Is there a way to build a filter that responds more quickly if that becomes an issue later on down the line? Ripple isn't a huge issue. This setup was supposed to have 34 mV of ripple which is totally ok. Ripple up to ~200 mV is acceptable.

So, I'll probably add more as I can think of it but that should be the jist of it...

What follows is my (messy) code that runs on the arduino.

/*Jason LaGuidice
  Program to read MAF voltage and export pulse width voltages.
  This is meant to take place of a MAF translator and allow manual
  adjustment of AFRs without replacing any other tuning software.
  Future revisions will check TPS voltage and only apply modification at WOT.

int sensorPin = 14;    //select analog pin 0 for MAF voltage input
int ecuPin = 3;       //select pin to output voltage via PWM
int sensorValue = 0;   //variable to store value coming from sensor
int modifiedValue = 0; //variable for the modified MAF voltage

void setup() {
  //declaration of the ecu pin as an analog output is NOT NECESSARY
  pinMode(sensorPin, OUTPUT);
  //use MAF reference voltage as reference voltage, just as the ECU does, connect to AREF
  //temporarily not used until a stable 5v reference can be found, once found
  //set to EXTERNAL

void loop() {
  //read MAF voltage
  sensorValue = analogRead(sensorPin);
  //if voltage is too (aka car not running) fake a singal for easy starting
  if(modifiedValue < 120)
    modifiedValue = 125;
  //if below 3v, increase voltage for driveability. this section may be replaced
  //and omitted once WOT detection is added
  else if(modifiedValue > 615)
    modifiedValue = sensorValue + 100;
    modifiedValue = int(sensorValue * 1.8);
  //at high loads. The reason the multiplication factor is smaller here is because
  //the low pass filter output based on duty cycle doesn't seem to be linear :( More
  //resolution of values will be necessary. Does the Arduino support log curves?
  //if not, can a reference table like in an actual ECU be used to determine multiplication
    modifiedValue = sensorValue + 100;
    modifiedValue = int(sensorValue * 1.4);
    //output to ECU - analogRead values go from 0-1023 while analogWrite values go from 0-254
  analogWrite(ecuPin, modifiedValue / 4);

I want to read a variable 0-5v DC signal (which will never actually reach 5v in use), multiply it by a number, and send it back along - and there's a ~100ms time constraint. Help? :slight_smile:

Edit: As a small update. I noticed that no matter what I did, I was getting the same results when multiplying values to be written out to the low pass filter. I did test the filter with a demo program that just stepped up the PWM duty cycle over time, it worked great.

I looked over my program again and noticed I had an error (pinMode(sensorPin, OUTPUT);)... so I set that to INPUT, and now my Arduino won't put out any voltage on any of the out pins, either via PWM or digital. The only one that works is the on-board LED. So it seems as if I've killed it somehow even though I was working with low voltages. Ugh.

Anyway, still, does anyone have any suggestions?

A couple of things:

  • You have to write pinMode(ecuPin, OUTPUT);

  • You have code like the following, which is assigning the same variable two lines in a row, so the first assignment has no effect:

    modifiedValue = sensorValue + 100;
    modifiedValue = int(sensorValue * 1.8);

Don't you mean:

    modifiedValue = sensorValue + 100;
    modifiedValue = int(modifiedValue * 1.8);
  • Your 6.85k resistor and 47uF capacitor have a time constant of 322ms, too slow by your requirements. This forms a filter with cutoff frequency of 1/(2pi6.85k*47uF) or 0.5 Hz, which is good but too good, given that the analogWrite() frequency is about 500 Hz. If you take the 47uF cap down to 4.7uF your filter will be 5 Hz (good enough) and your time constant goes down to 32.2ms, much better for what you want.

  • Finally, how high do you expect the voltage to go? You are never going to get above 5V with this scheme. Are you expecting 6V or higher (5V*1.2)?

Check out our new shield:

Much appreciated. The explanation on the formula helps a little too. I still don't understand all of it but you've got me going in the right direction.

I'm modifying the same number twice because I wanted to add .5v (100 counts) right from the start because I couldn't seem to hit 5v through the filter. If the output is lower than the original voltage, the car will run very poorly because the ECU won't add enough fuel. I was being on the safe side, hoping the car would idle. Instead though I was getting a voltage out that was too low regardless of what I did. So the first assignment in this case has no effect? I was hoping it would add 100 first, then multiply. I kept it on two separate lines so I could remove/adjust it more easily. It's just visual I guess because program space for something this simple isn't an issue.

I'm not expecting to see anything above 5 volts on the final output. The car's ECU won't accept anything higher than 5v anyway. Once the car was running I planned on having it check for a duty cycle above 1024, if that was the case, just limit it back to 1024 max (maxing out the sensor).

The car's ECU reads the voltage from the MAF (reads it into counts, 0-1023), looks that up in a table. It corresponds to a certain mass of air entering the engine. The ECU has tables on the fuel side of things too, and uses the mass of air entering to calculate exactly how much fuel to add to hit a stoichiometric mixture (or otherwise). What I'm trying to do is fake a higher signal, so the ECU thinks there's more air entering the engine, so it will add extra fuel. The car didn't come with a turbo and MAF sensors can't account for -density- of pressurized air. Since we're forcing 10psi into it, the air is a little more dense, so we need to add more fuel to compensate. In either case, the stock MAF only can output 5v maximum, so the ECU has no information for anything above 5v (if it sees higher than 4.95 or so, it will count it as a failed MAF anyway and throw a check engine light). But I'm ranting :slight_smile:

Thanks for the tips. I need to figure out how to get my Arduino's outputs working again. The lowest voltage that the system needs to handle is around .7-1v (engine idle), and the maximum is unknown, but 4.85 is good enough.

I kept it on two separate lines so I could remove/adjust it more easily.

I think you have missed the point. The way it is written that first line could be anything, it is TOTALLY over ridden by the next line.
So the sequence:-
modifiedValue = sensorValue * 600 - 12345;
modifiedValue = int(sensorValue * 1.8);
Would have the same effect as what you wrote.