Solved: Convert automotive temp & pressure sensor output to control PWM fan

Background:
I have a customized electric PWM cooling fan setup on my truck. The OEM engine computer is capable of operating a PWM fan based upon inputs of either coolant temp or AC pressure (GM 0-5VDC). I use an aftermarket software product to modify its settings. I added a PWM fan- it replaced an electro-mechanical fan.

Issue:
The engine computer operates the fan based upon coolant temp, but not for AC pressure. Using scanning software that reads the engine computer, I see both volts and pressure from the AC sensor, but the fan never comes on. I have reached out to the software vendor, and am awaiting help.

In the meantime, it occurred to me that an Arduino may be capable of solving the problem. I saw a post dealing with converting volts to PWM here, but that looks to be a basic linear conversion.

Desired goal:
What I need to do is have the fan operate at a slower speed as pressure builds, and then at some point max out and remained maxxed out. As such, I cannot use a linear output. As a basic idea of what I need to do, here are some numbers:

Parameters:
Pressure sensor fan range:
100-350 PSI (it operates up to 500 PSI, but a moot point beyond 350, see below)

Pressure sensor is 3-wire: 5V reference, ground, and signal.

PWM duty cycle (higher number is lower fan speed, with 90 being minimum and 15 being maximum, outside that range the fan stops) would need to be something like 90% for 110 to 120 PSI, then 80% for 121-135, then 70% for 136-150 and so on until it hits 15% at around 230 PSI. The pressure switch shuts down the AC compressor @ 350 PSI and reopens it @ 229.

Questions:

  1. Is this a viable project for an Arduino, taking into consideration it would need to be in a sealed case under hood?
  2. Would it be possible to power the Arduino from the 5V reference signal? It is used for several engine sensors.

Any constructive advice is welcome.

Even the smallest Arduino will do, provided that the environment (temperature...) is within supported limits.

You already have shown the algorithm: split the entire range into subranges and map the inputs into PWM cycles (analogWrite).

For a first test take any Arduino that you can program and run one of the example sketches that translate a pot voltage into a PWM duty cycle. If you can control the fan this way then go on for the final build.

Thanks. I'll break out the Simudino and work on this.

No. The whole point of a reference signal is to provide a reference, not to provide power. If you draw a significant current from the reference signal, it's voltage could drop, leading to inaccurate sensor readings from the Arduino and the other sensors too. Buy a 12V-5V "buck" converter designed for use in vehicles, and use this to power the Arduino.

The output of this buck converter won't exactly match the 5V reference signal, but it should be within a few tenths of a Volt at most. You can connect the reference signal to the Vref pin of the Arduino to compensate for that difference if you want, but I don't know how much difference it will make in practice. This isn't a piece of scientific equipment, after all!

For a first test take any Arduino that you can program and run one of the example sketches that translate a pot voltage into a PWM duty cycle. If you can control the fan this way then go on for the final build.

I ran the following basic analog input to PWM output (LED in this case) on my Simulator For Arduino (Uno mode) and it returned an error: cannot evaluate analogread(analogpin)

int ledPin = 9;      // LED connected to digital pin 9
int analogPin = 3;   // potentiometer connected to analog pin 3
int val = 0;         // variable to store the read value

void setup() {
  pinMode(ledPin, OUTPUT);  // sets the pin as output
}

void loop() {
  val = analogRead(analogPin);  // read the input pin
  analogWrite(ledPin, val / 4); // analogRead values go from 0 to 1023, analogWrite values from 0 to 255
}

Is this something I'm missing or a problem with the simulator? It does the same thing when I run a basic serial print sketch.

It looks fine, so I'll guess it's a simulator issue. Do you have to configure it to tell it what voltage it should be showing on that pin?

The simulator gives me an input panel with sliders for A0 through A5. I uninstalled the older version and have downloaded a newer one, I'll see if that makes a difference.

Try

int analogPin = A3;

The file update cured the issue, the sketch runs OK now.

However my needs have changed- what I need to do now is bypass the engine computer's fan control entirely. This means I need (if possible) the Arduino to have dual analog inputs with the single PWM output. To complicate matters some more, while the AC pressure sensor is a 3-wire 0-5V unit, the engine coolant sensor is a 2-wire resistance-based unit. I did find a resistance table for it, so I have a good data source for setting up the values I'll need at some point. I plan on collecting AC pressure data vs volts via my scanner while the engine is running so I can make a table for it as well.

I'm trying to learn on implementing this, but it is slow going.

Here is what I'm starting with:

//This file is meant to accept input from two automotive sensors and then convert their data to a PWM signal to operate a cooling fan as needed, based upon either temperature or pressure.

int ectPin = A0;     //analog input 0 Engine Coolant Temp or ECT
int acpPin = A1;     // analog input 1 AC Pressure or ACP

int val0 = 0;           // variable to store the value read of a0
int val1 = 0;           // variable to store the value read of a1
int pwmPin = 5;        // PWM output connected to digital pin 5

void setup() {
  pinMode(pwmPin, OUTPUT);  // sets the PWM pin as output
}
void loop() {
  val0 = analogRead(ectPin);  // read the ECT input pin
  analogWrite(pwmPin, val0 / 4); // analogRead values go from 0 to 1023, analogWrite values from 0 to 255
}

As written, this allows me to vary the A0 value (val0) and see a corresponding change on the PWM output. :grinning:

I then tried to add in the second sensor via a Val1 setting:

int ectPin = A0;     //analog input 0 Engine Coolant Temp or ECT
int acpPin = A1;     // analog input 1 AC Pressure or ACP

int val0 = 0;           // variable to store the value read of a0
int val1 = 0;           // variable to store the value read of a1
int pwmPin = 5;        // PWM output connected to digital pin 5

void setup() {
  pinMode(pwmPin, OUTPUT);  // sets the PWM pin as output
}
void loop() {
  val0 = analogRead(ectPin);  // read the ECT input pin
  val1 = analogRead(acpPin);  // read the ACP input pin
  analogWrite(pwmPin, val0 / 4); // analogRead values go from 0 to 1023, analogWrite values from 0 to 255
  analogWrite(pwmPin, val1 / 4); // analogRead values go from 0 to 1023, analogWrite values from 0 to 255
}

When I do that, the only sensor read on the simulator is ACP. When I move the voltage slider for ECT, it looks like the PWM value is pulsing.

If I can get the dual inputs sorted, then I can move on to learning about conditional text so I can properly vary the PWM based upon the input value.

Thanks for any help on this project.

If you use both values to control the pwmPin you will get a continuous mix of both settings.

Thanks for the reply.

What I need to have happen is the highest value go to the PWM output. For example, if my coolant temp is below operating level, the AC pressure could still be high enough to where the fan is needed. In that event, if the ECT is being output, then the AC could over-pressure. Ditto the inverse in cooler weather- the coolant could be hot w/o the AC being on.

I guess what I need is a guru to tell me if what I'm trying to do is possible with this hardware. Sorry for my lack of knowledge, but I'm here to learn.

You never touched the hardware, it will have to be constructed after your software problems are fixed.

My apologies for not wording that correctly. I know the device has sufficient inputs and a PWM output, my question is if 2x analog inputs can be sent independently to 1x output.

They can if that makes sense. If you say you want to use the higher value, then use it. See if-statement how to determine the higher of two values in code.

After doing some more reading, I have written a sketch that should allow for two inputs and two fan speeds per input as well as an off setting for the fan. However the PWM output flickers when I vary the input depending upon what each sensor is reading (for example, if the ECT is in the 0 range and the ACP is in the 200 range, it flickers from 200 to 000. Adding the dual analogreads and10ms delays seemed to help with this. When both are at 1023, the PWM is 0, which is a desired setting.

When I ran this as a single input, it worked as designed. My simulator has sliders that allow for 0-1023 values on the analog pins, and it shows the 0-255 output on the digital pins.

I'm using the && operators in my IF statement to define ranges for each sensor, and will expand upon this once it is working as desired.

Question: Any advice on smoothing the output or locking it to the lowest value above 0? I've read about the MIN and MAX functions, but am not sure how to make them chose the lower of the two values between valect and valacp (if this is even possible).

//Sketch to take input from two automotive sensors and use them to operate a PWM cooling fan
int ect = 0; // Engine coolant temp sensor on A0
int acp = 1; // AC pressure sensor on A1
int pwmPin = 5; // Output pin for PWM
int valect = 0; // Variable to store the ect read value 
int valacp = 0; // Variable to store the acp read value
void setup(){
}
void loop(){
// read the value on analog input
valect = analogRead(ect);
delay(10);
valect = analogRead(ect);
delay(10);

valacp = analogRead(acp);
delay(10);
valacp = analogRead(acp);
delay(10);

if ((valect>=800) && (valect<900)) { 
//Step 1 ect value, 0-1023
analogWrite(pwmPin, 200); //Proportional step 1 pwm output
}
else if (valect<800) { 
//Step 2 ect value, 0-1023
analogWrite(pwmPin, 100); //Proportional step 2 pwm output
}
else {
analogWrite(pwmPin, 0); //Fan off
}

if ((valacp>=600) && (valacp<900)) { 
//Step 1 acp value, 0-1023
analogWrite(pwmPin, 200); //Proportional step 1 pwm output
}
else if (valacp<600) { 
//Step 2 acp value, 0-1023
analogWrite(pwmPin, 100); //Proportional step 2 pwm output
}
else {
analogWrite(pwmPin, 0); //Fan off
}

}

Pick which of the sensors has the highest value using an if. Use that value to drive the PWM.

As it is, they're fighting with each other.

You likely also want to use millis to ensure that the decision as to which input is in charge only changes one a minute (or whatever suits you).

First advice: Calculate the value before you apply it to the pwmPin, like this

if (valect...)
  pwmOut = ...;
if (valacp...)
  pwmOut = ...;
analogWrite(pwmPin, pwmOut);

Now all stutter is eliminated :slight_smile:

Second advice: select the dominant input as the only control source:

if (valect ... valacp) //here ... can be <, >, <= etc.
  pwmOut = valect ...;
else
  pwmOut = valacp...;
analogWrite(pwmPin, pwmOut);

Great advice, thanks.

EDIT
Here's the latest version so I don't keep loading up this thread with failed examples. When I run this on the simulator, I get an "Unknown command, Line 34:pwmOut, 0; //Fan off below 211 degrees" error (same with the other fan off line, but none of the other pwmOut lines). If I comment out the offending lines, it works until another pwmOut is called, then it stops and I get another error message. I think this is an issue with the simulator. Since the PWM wire for the fan on my truck is easy to get to, I'm going to wire up an Uno with a 5V power supply and use a pot on the inputs to see if it works.

This example also has the math for the coolant and pressure sensors as well as calculated values for 4x levels of fan control.

// Sketch to take inputs from two automotive sensors and operate a PWM cooling fan. 
// Sensors are a GM 2-wire coolant temperature sensor and a 3-wire AC pressure sensor
int ect = 0;
int valect = 0; // Variable to store ect read value
float R1 = 10000; // Resistor value for voltage divider for temp sensor
float logR2, R2;
int ectvolts = 0; // Variable to store converted output
int acp = 1; // AC pressure sensro on pin A1
int pwmPin = 5; // Output pin for PWM
int valacp = 0; // Variable to store acp read value
int acpvolts = 0; // Variable to store converted output
int pwmOut = 0; // Variable to store sensor output to run fan at different speeds and conditions

void setup() {
  // None required
}

void loop() {
  // Read the value on analog input, math added for NTC sensor
  ectvolts = analogRead(ect); 
  valect = (R2 = R1 * (1023.0 / (float)ectvolts - 1.0)); // Converts resistance to usable input data

  // Read the value on the AC pressure sensor, math added to convert to steps
  acpvolts = analogRead(acp); // Reads the value on acp pin and gives it a name
  valacp = (float)acpvolts * (5.0 / 1023.0); //Converts analog input into steps
 
  if (valect < valacp) pwmOut = valect; // Compares sensors so both are not competing for output, lowest value used
  else pwmOut = valacp;
  
analogWrite(pwmPin, pwmOut); //Outputs to PWM pin

// Coolant temp fan activation and PWM duty cycles
if (valect <= 753) 
pwmOut, 0; // Fan off below 211 degrees
if ((valect >= 754) && (valect <= 790)) 
pwmOut, 192; // Fan at 25% at about 212-220 degrees F
if ((valect >= 781) && (valect <= 807)) 
pwmOut, 128; // Fan at 50% at about 221-230 degrees
if ((valect >= 808) && (valect <= 834)) 
pwmOut, 64; // Fan at 75% at about 230-239 degrees
if (valect >= 835) 
pwmOut, 30; // Fan at 100% at over 240 degrees

// AC pressure fan activation anf PWM duty cycles
if (valacp <= 286) 
pwmOut, 0; // Fan off below 158 PSI
if ((valacp >= 287) && (valacp <= 403)) 
pwmOut, 192; // Fan at 25% at about 159-199 PSI
if ((valacp >= 404) && (valacp <= 463)) 
pwmOut, 128; // Fan at 50% at about 200-229 PSI
if ((valacp >= 464) && (valacp <= 503)) 
pwmOut, 64; // Fan at 75% at about 230-249 PSI
if (valacp >= 504) 
pwmOut, 30; // Fan at 100% over 250 PSI

}


Please follow the given pattern: one single analogWrite instead of 5 will prevent any stutter.

When will you start to try to understand the code you use?