What's new

Open Source PWM Fan Controller

The Nano has an LM1117 linear regulator. It's going to dissipate a lot of heat for 12+v input and probably cook itself eventually. It's also not reverse-polarity protected or load-dump protected for automotive use. I happen to have a PWM fan controller I designed on my desk next to me right now that I've been polishing the code on. I can share code/schematics if you want, but read on:

When you "force" the fan to max, that's probably actually 95% duty cycle, not 100%. The designed range of DC for that fan is likely 5-95% or 10-90%. It's done like this so if there's a shorted wire or a broken wire the fan controller (in the fan itself) knows there's a problem and runs the fan at full speed. If you sent the PWM output to 100% you're telling the fan something is wrong and it might not react the way you want.

The input to the fan is probably one that wants a simple "switched ground" type output from your Nano. You would wire up a simple NPN transistor on your Arduino GPIO pin and between ground and the fan's input. PWM switches this ground on and off. This is why the voltage doesn't matter, too.

I think your control strategy lacks a bit... If you really just want the fan to come on at 160 and haul ass for no reason then so be it. But a better way is to set up a lookup table in your program that applies different DC's to different temp sensor values. At 155F you might need 30% to keep it cool. Put 5 degree steps in the table. At 170F it might need 60%, etc. You will have to run it a bit in-situ, but once you do this you'll know how much fan you really need at these different temps. Running the engine at 160F is a fruitless tree because you'll be fighting your thermostat all the time and running the fan way more than you need to. You want an engine thermostat that's 10-20F lower than your temp setpoint. That probably means you should run a 160F thermostat and set your coolant target temp to 180F, something like that. You will learn that the relationship between the fan speed and engine temp is pretty damned linear, actually. Simple, but took a while to learn this.

Also, you would be smart to switch to a two-wire temp sensor to avoid all the garbage that can come in from sharing a chassis ground with the single wire. The typical GM sensor is cheap and the datasheet has all the values already. I can share my lookup tables if you want.

You don't need the manual override switch or the pot. You are trying to deal with a situation that won't exist in real life. If you want the fan to run at full speed all the time just disconnect the PWM input and watch it fly.
I'm very interested if you want to share you're schemes and code for the Arduino Nano.

I have A 2000 Dodge Dakota 4.7L and i'm on the same path off building my own fan controller with an Arduino Nano.
Bought one and will be here next week.

I'm from the Netherlands, and there are zero parts available for the Dakota overhere.
My fan clutch died on me and my original A/C fan was broken already.

instead of trying to find or repair my clutch fan, i bought a used Mercedes ML series fan 850W PWM controlled fan and put it in the Dodge.

Currently and temporarily i let it run on the Fail Safe off the fan, the Fan output off the Dodge PCM is providing power to the Fail Safe wire on the Mercedes Fan, because i haven't figured out yet how to program the Arduino Nano.

I'm trying to make a program for the Nano, and i want to use the inputs of the A/C High Pressure Switch for A/C control and the input of the ECT sensor for engine temp control.

I've never had any experience with any kind of Arduino, so i could use some help....
 
I don't have any "Arduino" code to share, but it all works pretty much the same as the C I'm using. An Arduino is super handy to write code and bench test something, but it's not a complete solution. I've always wanted to make an automotive arduino board and sell it. It needs a real power supply, CAN bus, and a few GPIO with some protection/filtering. And it needs to have some kind of nice sealed enclosure, not some 3d printed stuff. My own projects go into an automotive sealed enclosure with Molex or TE sealed connectors, and i do my own PCB's as well.

Here's the lookup table for temp in F from the GM 2-wire temp sensor. The values are in ADC counts, so that's 0-1023. I had to "linearize" the data here so I had even temperature steps. I'm using a resistor voltage divider here with a 330 ohm top resistor and the sensor goes to ground. This is confusing to look at the first time, lookup tables in general, but once you get it the math is simple and you end up using it for almost everything.

Code:
int16_t const __flash CTS_Temp_TableF[] =  // 39 entries, steps size 24, offset 138, left col is temp in F * 10, ADC count in comments
{   
    2955, // 138
    2815, // 162
    2698, // 186
    2592, // 210
    2496, // 234
    2412, // 258
    2334, // 282
    2262, // 306
    2194, // 330
    2129, // 354
    2068, // 378
    2008, // 402
    1953, // 426
    1897, // 450
    1843, // 474
    1792, // 498
    1742, // 522
    1692, // 546
    1641, // 570
    1591, // 594
    1540, // 618
    1490, // 642
    1438, // 666
    1387, // 690
    1333, // 714
    1279, // 738
    1225, // 762
    1166, // 786
    1105, // 810
    1040, // 834
    968, // 858
    891, // 882
    801, // 906
    702, // 930
    577, // 954
    410, // 978
    140, // 1002
    320, // 1026
    320  // 1050
};

And here's the code that pulls the temp from that "table" based on the ADC value. Yeah it's a little ugly right now, it will get cleaned up later, but this is easy to follow :)

Code:
int16_t lookup_CTS_F(uint16_t ADCRef) // get temperature in F from GM temp sensor lookup table
{
    int16_t y=(ADCRef-138); // apply offset of table
    y = y / 24; // apply table step size
    int16_t x=CTS_Temp_TableF[y]; // find low value
    int16_t xx=CTS_Temp_TableF[y+1]; // find high value
    int16_t xxx=(xx-x)/24; // interpolate between values
    int16_t xxxx=ADCRef-(((y*24)+138));
    y=x+(xxxx*xxx); // add interpolated value
    if (y%10 < 5) y = y/10; //round remainder
    else y = y/10 + 1;    //round remainder
return y;   
}

Here's a simple lookup table for the fan output DC. Once you know the measured temperature you do some simple math to figure out which position in this array you want, the same as with the ADC and temp above. In this example the set temp in my application is 179F. The math also interpolates between values.

Code:
uint8_t const __flash fan_duty_table[] = // PWM engineFanDC output vs. temp.  Offset is 160F, 5F steps
{
    0,      //160F
    0,      //165
    0,      //170
    30,    //175
    40,    //180
    50,    //185
    75,    //190
    85,    //195
    100   //200F
};
 
Well, thanks for sharing, i'm learning about Arduino programming and it's using the same "C" language to program.
I've already made my ADC tables for A/C pressure and Temp to ADC.
Now i have to write a program for the Arduino, i made one and put it in the Arduino IDE, by the looks of it it should work.
Although my calculations for pressure and temp look very different as the ones you showed.
I've read there's a lot of ways you can program and use strings, arrays, if, else and so on.
But those calculations are very difficult for me, because this is my first attempt it's not easy for me.
I'll get my Arduino tonight and will try to upload my Sketch and see if it will work.
 
So I had to pause while I worked on more important stuff, but my jeep let me know it’s time by springing a pinhole leak in the radiator yesterday. So the new fan is going in along with the new rad soon. Coincidentally, I happened to have the final (I think lol) case being printed when I discovered the leak. Crazy how timing works out sometimes. Anyhow it has a few refined dimensions, the 3rd bulkhead connector to inform the controller when the AC is operating, and printed in black.

I’m going to have to refresh myself on the code again, but it’s right there. Just need to add the final bit for the AC mode.

And I still plan to upload the CAD files of the case, the full hardware list, and the final version of the code for anyone who has interest in making one of these things

IMG_9494.jpeg


IMG_9492.jpeg
 
Last edited:
Hey Treefrog , could I bug you one more time on this project? I kinda fell out of the groove after not looking at any code in the last month, but attempting to refresh myself and get it wrapped up. The updates are:

-deleted that nonsense analogWrite that was leftover in the code
-implemented your duty cycle code right before the pwmWrite as you noted
-set duty cycle to zero for all current_duty below the defined minimum
-have it subtracting from 100 to flip the numbers to match the input the fan is looking for
-added a new digital input pin with pullup resistor for the AC detection circuit, and added a modifier right in the middle of your code, so it's still being constrained by the min and max.

I think there's a decent chance I butchered something with these modifications, is there any way I could request you set it up on the breadboard again to test the outputs are working as intended? I'd definitely owe you a :beer:, if not a whole case :grinpimp:

FLAWED, IGNORE THIS CODE

C-like:
// Include required libraries
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <PWM.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

// Initialize OLED display object
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// Define constants for pins and values
const int FAN_PIN = 9;               //Fan output pin
const int AC_PIN = 8;                //AC detection pin
const int TEMP_SENSOR_PIN = A1;      //Temp sensor pin
const int POT_PIN = A2;              //Potentiometer pin
const int buttonPin = 11;            //Momentary switch pin
const int MIN_TEMP = 160;            //Minimum duty cycle temperature
const int MAX_TEMP = 210;            //Maximum duty cycle temperature
const int MIN_DUTY = 9;              //Minimum duty cycle percentage
const int MAX_DUTY = 85;             //Maximum duty cycle percentage
const int DUTY_INCREMENT = 1;        //Duty cycle percentage increments
const int POT_RANGE = 10000;         //Potentiometer resistance

// Define variables for current values
int current_duty = 0;                //Current duty cycle
int current_screen = 1;              //Current screen (may be deleted)

int current_pot_value = 0;           //Current raw potentiometer value
int mapped_pot = 0;                  //Potentiometer values mapped to range of pMin to pMax
int pMin = -20;                      //Minimum temperature variation for minimum potentiometer value
int pMax = 20;                       //Maximum temperature variation for maximum potentiometer value

int current_temp_sensor = 0;         //Current raw temperature sensor output
int mapped_temp = 0;                 //Temperature sensor values mapped to Fahrenheit range
int tuned_MIN_TEMP = 0;              //Minimum temp range after modification by potentiometer value
int tuned_MAX_TEMP = 0;              //Maximum temp range after modification by potentiometer value

int buttonPushCounter = 0;           //Counter for the number of button presses
boolean buttonState = LOW;           //Current state of the button
boolean lastButtonState = LOW;       //Previous state of the button

// Set PWM Frequency
int32_t frequency = 100;             //Frequency (in Hz)

void setup() {
  // Initialize serial communication
  Serial.begin(9600);

  // Set up momentary switch as input
  pinMode(buttonPin, INPUT_PULLUP);

  // Initialize OLED display
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);

  //Prepare Timers for PWM frequency on FAN_PIN, activate onboard LED (pin 13) if successful
  InitTimersSafe();
  bool success = SetPinFrequencySafe(FAN_PIN, frequency);
  if(success) {
    pinMode(13, OUTPUT);
    digitalWrite(13, HIGH);
  }
}

void loop() {
  // Read current potentiometer value
  current_pot_value = analogRead(POT_PIN);

  //Map potentiometer data to a range from negative (-)30 to +30, a simple variable added to the commanded temperature range
  mapped_pot = map(current_pot_value, 1023, 0, pMin, pMax);

  tuned_MIN_TEMP = (MIN_TEMP + mapped_pot);
  tuned_MAX_TEMP = (MAX_TEMP + mapped_pot);

  // Read current temperature sensor value
  current_temp_sensor = analogRead(TEMP_SENSOR_PIN); //Raw unmapped temp sensor data

  //Map voltages from temp sensor to Fahrenheit. First two numbers are observed resistance range, second two numbers are correlating temp in Fahrenheit
  mapped_temp = map(current_temp_sensor, 497, 78, 63, 212);

  // Define function for updating the duty cycle based on temperature and potentiometer
  current_duty = MIN_DUTY + (MAX_DUTY - MIN_DUTY) * (mapped_temp - tuned_MIN_TEMP) / (tuned_MAX_TEMP - tuned_MIN_TEMP);
  current_duty = round(current_duty / DUTY_INCREMENT) * DUTY_INCREMENT;

  //Detect if AC is engaged
  pinMode(AC_PIN, INPUT_PULLUP);
  if (digitalRead(AC_PIN) == HIGH) {current_duty + 20;}

  //Duty cycle under minimum = zero, above max = defined maximum
  if (current_duty < MIN_DUTY) {current_duty = 0;}
  else if (current_duty > MAX_DUTY) {current_duty = MAX_DUTY;}

  //Output PWM to FAN_PIN based on calculated duty cycle
  pwmWrite(FAN_PIN, 100-(round(current_duty * 255.0 / 100.0)));

  // Read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  Serial.println(current_temp_sensor);
  Serial.println(mapped_temp);
  switch (buttonPushCounter) // choose what to display based on buttonPushCounter value
  {
    case 0:

      display.setTextColor(WHITE);
      display.clearDisplay();
      display.setCursor(0, 10);
      display.print("RG:");
      display.print(tuned_MIN_TEMP) ;
      display.print("-") ;
      display.println(tuned_MAX_TEMP);
      display.display();
      break;


    case 1:

      display.setTextColor(WHITE);
      display.clearDisplay();
      display.setCursor(0, 10);
      // Display static text
      display.print("C.Temp:");
      display.println(mapped_temp);
      display.display();
      break;

    case 2:

      display.setTextColor(WHITE);
      display.clearDisplay();
      display.setCursor(0, 10);
      // Display static text
      display.print("C.Duty:");
      display.println(current_duty);
      display.display();
      break;

  }

  if (buttonState != lastButtonState)
  {
    if (buttonState == HIGH)
    {
      // if the current state is HIGH then the button
      // went from off to on:
      buttonPushCounter++;  // add one to counter
      display.clearDisplay();
      display.display();
      if (buttonPushCounter > 2)
      {
        buttonPushCounter = 0;
      }

    }
    // save the current state as the last state,
    //for next time through the loop
    lastButtonState = buttonState;
  }

}
 
Last edited:
Hey Treefrog , could I bug you one more time on this project? I kinda fell out of the groove after not looking at any code in the last month, but attempting to refresh myself and get it wrapped up. The updates are:

-deleted that nonsense analogWrite that was leftover in the code
-implemented your duty cycle code right before the pwmWrite as you noted
-set duty cycle to zero for all current_duty below the defined minimum
-have it subtracting from 100 to flip the numbers to match the input the fan is looking for
-added a new digital input pin with pullup resistor for the AC detection circuit, and added a modifier right in the middle of your code, so it's still being constrained by the min and max.

I think there's a decent chance I butchered something with these modifications, is there any way I could request you set it up on the breadboard again to test the outputs are working as intended? I'd definitely owe you a :beer:, if not a whole case :grinpimp:
I will try running it tomorrow. If I am reading that correctly, the pin for AC is high when AC is on? And button high to switch screens?

What is the *255.0/100.0 in the pwmWrite for? And as a side note, outside of debugging on the bench, you should comment out or remove the serial writing. It will likely speed the code up some.
 
You rock. Yeah, pin 11 uses the internal pullup resistor, and completes a ground when the momentary switch is pressed for toggling the screen modes (that hasn't changed since your last breadboard test). I've now assigned pin 8 to be AC_PIN, which also uses the internal pullup, and should activate when grounded to add 20% to the duty cycle (if I did it correctly?)

IIRC the *255/100 is converting the duty cycle percentage into the max bit range of that pin? It has been in there since the pwmWrite was introduced, but I need to refresh myself further on it.

Great note on the serial writing, I'll go ahead and comment it out
 
You rock. Yeah, pin 11 uses the internal pullup resistor, and completes a ground when the momentary switch is pressed for toggling the screen modes (that hasn't changed since your last breadboard test).
Cool. And I actually have buttons this time.
I've now assigned pin 8 to be AC_PIN, which also uses the internal pullup, and should activate when grounded to add 20% to the duty cycle (if I did it correctly?)
Taking another quick look at this code, I don't think it is done correctly. You currently have setting the pin to pull up done every loop. It would probably by a good idea to move it to setup where the button pull up is. The adding 20 is wrong. You are adding 20 but not assigning it to anything. It should either be current_duty = current_duty + 20 or current_duty+=20.
IIRC the *255/100 is converting the duty cycle percentage into the max bit range of that pin? It has been in there since the pwmWrite was introduced, but I need to refresh myself further on it.

Great note on the serial writing, I'll go ahead and comment it out
I'll need to review the function as well. I am not familiar with it. But converting it makes sense. But if you are trying to invert it from 0-100 to 100-0, the 100- needs to be in parentheses with the duty cycle instead of on the converted value.
 
Cool. And I actually have buttons this time.

Taking another quick look at this code, I don't think it is done correctly. You currently have setting the pin to pull up done every loop. It would probably by a good idea to move it to setup where the button pull up is. The adding 20 is wrong. You are adding 20 but not assigning it to anything. It should either be current_duty = current_duty + 20 or current_duty+=20.

I'll need to review the function as well. I am not familiar with it. But converting it makes sense. But if you are trying to invert it from 0-100 to 100-0, the 100- needs to be in parentheses with the duty cycle instead of on the converted value.

Haha win on the button, though just tapping the wire worked just fine last time :laughing:

Great catch on the pin setup, I grouped it with the other line of AC related code because it was tidy, but forgot that meant it was in the loop versus part of the initial setup. Corrected now. I also see what you mean with the 20, I didn't define what I was actually trying to add 20 to. I believe I have that corrected now? And on the subtracting from 100, would it work like this?

Updated code with the above revisions:

C-like:
// Include required libraries
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <PWM.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

// Initialize OLED display object
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// Define constants for pins and values
const int FAN_PIN = 9;               //Fan output pin
const int AC_PIN = 8;                //AC detection pin
const int TEMP_SENSOR_PIN = A1;      //Temp sensor pin
const int POT_PIN = A2;              //Potentiometer pin
const int buttonPin = 11;            //Momentary switch pin
const int MIN_TEMP = 160;            //Minimum duty cycle temperature
const int MAX_TEMP = 210;            //Maximum duty cycle temperature
const int MIN_DUTY = 9;              //Minimum duty cycle percentage
const int MAX_DUTY = 85;             //Maximum duty cycle percentage
const int DUTY_INCREMENT = 1;        //Duty cycle percentage increments
const int POT_RANGE = 10000;         //Potentiometer resistance

// Define variables for current values
int current_duty = 0;                //Current duty cycle
int current_screen = 1;              //Current screen (may be deleted)

int current_pot_value = 0;           //Current raw potentiometer value
int mapped_pot = 0;                  //Potentiometer values mapped to range of pMin to pMax
int pMin = -20;                      //Minimum temperature variation for minimum potentiometer value
int pMax = 20;                       //Maximum temperature variation for maximum potentiometer value

int current_temp_sensor = 0;         //Current raw temperature sensor output
int mapped_temp = 0;                 //Temperature sensor values mapped to Fahrenheit range
int tuned_MIN_TEMP = 0;              //Minimum temp range after modification by potentiometer value
int tuned_MAX_TEMP = 0;              //Maximum temp range after modification by potentiometer value

int buttonPushCounter = 0;           //Counter for the number of button presses
boolean buttonState = LOW;           //Current state of the button
boolean lastButtonState = LOW;       //Previous state of the button

// Set PWM Frequency
int32_t frequency = 100;             //Frequency (in Hz)

void setup() {
  // Initialize serial communication
  //Serial.begin(9600);

  // Set up momentary switch as input
  pinMode(buttonPin, INPUT_PULLUP);

  // Set up AC pin to be grounded for activation
  pinMode(AC_PIN, INPUT_PULLUP);

  // Initialize OLED display
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);

  //Prepare Timers for PWM frequency on FAN_PIN, activate onboard LED (pin 13) if successful
  InitTimersSafe();
  bool success = SetPinFrequencySafe(FAN_PIN, frequency);
  if(success) {
    pinMode(13, OUTPUT);
    digitalWrite(13, HIGH);
  }
}

void loop() {
  // Read current potentiometer value
  current_pot_value = analogRead(POT_PIN);

  //Map potentiometer data to a range from negative (-)30 to +30, a simple variable added to the commanded temperature range
  mapped_pot = map(current_pot_value, 1023, 0, pMin, pMax);

  tuned_MIN_TEMP = (MIN_TEMP + mapped_pot);
  tuned_MAX_TEMP = (MAX_TEMP + mapped_pot);

  // Read current temperature sensor value
  current_temp_sensor = analogRead(TEMP_SENSOR_PIN); //Raw unmapped temp sensor data

  //Map voltages from temp sensor to Fahrenheit. First two numbers are observed resistance range, second two numbers are correlating temp in Fahrenheit
  mapped_temp = map(current_temp_sensor, 497, 78, 63, 212);

  // Define function for updating the duty cycle based on temperature and potentiometer
  current_duty = MIN_DUTY + (MAX_DUTY - MIN_DUTY) * (mapped_temp - tuned_MIN_TEMP) / (tuned_MAX_TEMP - tuned_MIN_TEMP);
  current_duty = round(current_duty / DUTY_INCREMENT) * DUTY_INCREMENT;

  //Detect if AC is engaged
  if (digitalRead(AC_PIN) == HIGH) {current_duty = current_duty + 20;}

  //Duty cycle under minimum = zero, above max = defined maximum
  if (current_duty < MIN_DUTY) {current_duty = 0;}
  else if (current_duty > MAX_DUTY) {current_duty = MAX_DUTY;}

  //Output PWM to FAN_PIN based on calculated duty cycle
  pwmWrite(FAN_PIN, round(100 - current_duty * 255.0 / 100.0));

  // Read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  //Serial.println(current_temp_sensor);
  //Serial.println(mapped_temp);
  switch (buttonPushCounter) // choose what to display based on buttonPushCounter value
  {
    case 0:

      display.setTextColor(WHITE);
      display.clearDisplay();
      display.setCursor(0, 10);
      display.print("RG:");
      display.print(tuned_MIN_TEMP) ;
      display.print("-") ;
      display.println(tuned_MAX_TEMP);
      display.display();
      break;


    case 1:

      display.setTextColor(WHITE);
      display.clearDisplay();
      display.setCursor(0, 10);
      // Display static text
      display.print("C.Temp:");
      display.println(mapped_temp);
      display.display();
      break;

    case 2:

      display.setTextColor(WHITE);
      display.clearDisplay();
      display.setCursor(0, 10);
      // Display static text
      display.print("C.Duty:");
      display.println(current_duty);
      display.display();
      break;

  }

  if (buttonState != lastButtonState)
  {
    if (buttonState == HIGH)
    {
      // if the current state is HIGH then the button
      // went from off to on:
      buttonPushCounter++;  // add one to counter
      display.clearDisplay();
      display.display();
      if (buttonPushCounter > 2)
      {
        buttonPushCounter = 0;
      }

    }
    // save the current state as the last state,
    //for next time through the loop
    lastButtonState = buttonState;
  }

}
 
Copy pasted the code in Arduino. Added the parentheses to pwmWrite. Worked correctly. It is a little bit disorienting having the PWM on the display be inverted from what the oscilloscope is saying.

One thing that occurred to me is the max voltage for a digital input is 5.5V. So running the AC pin off a 12 V switch may be a problem.

I looked into the pwmWrite *255. It is looking for an 8 bit number, not a percent. So the 255 handles that.
 
Hell yes, thank you very much for that! I could see the display vs oscilloscope difference being disorienting, though I figure it’s probably most intuitive for the end user to have the display go from low to high.

Hmm, interesting note on the max voltage of the digital pins, still a potential issue with it just completing a ground?

Makes sense on the 255 as well. I vaguely recall that, though all of this is still new enough to me that the month pause definitely set me back a few steps hah.

I also still need to see if the fan is going to recognize the lower voltage pwm signal, or if I’m going to have to step it up through a mosfet. I’ll see if I can test that part out tonight
 
Hell yes, thank you very much for that! I could see the display vs oscilloscope difference being disorienting, though I figure it’s probably most intuitive for the end user to have the display go from low to high.
Having the display as duty cycle that the fan wants is the better way of showing it.
Hmm, interesting note on the max voltage of the digital pins, still a potential issue with it just completing a ground?
You should be fine. I'm not used to using switches with pull up or pull down and had wired it the way I normally would switching between positive and ground. I just tried it with the switch as open or ground and it works fine.
Makes sense on the 255 as well. I vaguely recall that, though all of this is still new enough to me that the month pause definitely set me back a few steps hah.
Data types and conversions are way to easy to get confused by.
I also still need to see if the fan is going to recognize the lower voltage pwm signal, or if I’m going to have to step it up through a mosfet. I’ll see if I can test that part out tonight
Is the fan looking for a pwm waveform or is it wanting to sink to ground? I think Spals are the sinking type. The reason I ask is that if it is the sinking type, you may have 12 V from the fan and damage the board.
 
Having the display as duty cycle that the fan wants is the better way of showing it.

While it' makes sense for us now knowing what the fan wants, it just intuitively feels backwards to see a high number while the fan is running slow, and a low number while the fan is running at higher speeds. I may flip it, but I'm torn

You should be fine. I'm not used to using switches with pull up or pull down and had wired it the way I normally would switching between positive and ground. I just tried it with the switch as open or ground and it works fine.

Nice, sounds good there

Data types and conversions are way to easy to get confused by.

Haha yes, especially paired with the fact that I've been kinda brute forcing my way through this, so not every concept has fully clicked and become intuitive to me yet

Is the fan looking for a pwm waveform or is it wanting to sink to ground? I think Spals are the sinking type. The reason I ask is that if it is the sinking type, you may have 12 V from the fan and damage the board.

For the sake of my own learning, can you explain this part a bit further? I'm not sure I grasp what you're articulating here
 
While it' makes sense for us now knowing what the fan wants, it just intuitively feels backwards to see a high number while the fan is running slow, and a low number while the fan is running at higher speeds. I may flip it, but I'm torn
I'd leave it as 0 is no fan and 100 is all of the fan.
Nice, sounds good there

Haha yes, especially paired with the fact that I've been kinda brute forcing my way through this, so not every concept has fully clicked and become intuitive to me yet

For the sake of my own learning, can you explain this part a bit further? I'm not sure I grasp what you're articulating here
The fan's pwm may be a pull up or pull down resistor, instead of high and low being provided by the Arduino.
 
Alright so I learned some things today. First, the fan will not run off the 5v PWM coming out of the arduino, so it has to be stepped up to 12v. Second, I just blindly dropped a mosfet on the output and fried the arduino :laughing:. So I'm putting another arduino in (glad I got spares), and actually reading what I should do instead of winging it haha. First error was I didn't confirm that the mosfet was a logic level component that can be switched off 5v, and second it looks like I should have added resistor and/or diode on the PWM output from the arduino to protect it.

I've seen some references to stepping up the PWM signal from arduinos and everyone seems to have a different component list to recommend. I saw the IRL540 mosfet come up a few times, would you guys agree with that? Do you have a better/more readily available recommendation (this one looks like it may be outdated)? What should I be adding to the circuit to protect the Arduino?

https://www.vishay.com/docs/91300/irl540.pdf

How do I convert 0-5v PWM to a 0-12v PWM

Arduino 5v PWM to 12v PWM
 
Alright so I learned some things today. First, the fan will not run off the 5v PWM coming out of the arduino, so it has to be stepped up to 12v. Second, I just blindly dropped a mosfet on the output and fried the arduino :laughing:. So I'm putting another arduino in (glad I got spares), and actually reading what I should do instead of winging it haha. First error was I didn't confirm that the mosfet was a logic level component that can be switched off 5v, and second it looks like I should have added resistor and/or diode on the PWM output from the arduino to protect it.

I've seen some references to stepping up the PWM signal from arduinos and everyone seems to have a different component list to recommend. I saw the IRL540 mosfet come up a few times, would you guys agree with that? Do you have a better/more readily available recommendation (this one looks like it may be outdated)? What should I be adding to the circuit to protect the Arduino?

https://www.vishay.com/docs/91300/irl540.pdf

How do I convert 0-5v PWM to a 0-12v PWM

Arduino 5v PWM to 12v PWM
I would just go with what those forums say. Probably a good idea to use the flyback diode that is mentioned in one of those links. I don't know much about trying to protect an arduino. If you are adding a diode to the output, make sure that it can handle the switching frequency.
 
I would just go with what those forums say. Probably a good idea to use the flyback diode that is mentioned in one of those links. I don't know much about trying to protect an arduino. If you are adding a diode to the output, make sure that it can handle the switching frequency.

Cool, and good note on double checking the frequency the flyback diode can handle. I rebuilt the unit with a new Arduino so it's back up and running, and I got some logic level mosfets so I'm about ready for the next attempt. Learning with every step though, so no regrets!
 
A buddy reminded me of this failure of mine today on a call...
How are you making out?
 
Ahhh I didn't read everything posted "Recently" here, but you should put a simple transistor on the PWM output that switches to ground. That's what all the automotive stuff does and the voltage doesn't matter. Flyback diodes and all that other BS are not required unless you're making an motor driver, but that's what is already built into these fans we're talking about. I can make you up a little drawing later if this doesn't make sense.
 
A buddy reminded me of this failure of mine today on a call...
How are you making out?

I made it to winter without the fan upgrade :laughing:. I still need to wrap it up.

Ahhh I didn't read everything posted "Recently" here, but you should put a simple transistor on the PWM output that switches to ground. That's what all the automotive stuff does and the voltage doesn't matter. Flyback diodes and all that other BS are not required unless you're making an motor driver, but that's what is already built into these fans we're talking about. I can make you up a little drawing later if this doesn't make sense.

Hmmm I need to go back and revisit where I left off, but that sounds very interesting!
 
Now that is super interesting, so basically a less delicate arduino. Something like that would probably be more reliable in an automotive environment. I still plan to give it a go with the standard off the shelf stuff once I turn focus back to it, but I'll definitely report here if I fry 'em
Yeah might be useful if we ever get better at coding all these automotive projects.
 
Welp, radiator sprung a leak, so the new fan is going in with the new radiator. Refreshing myself on everything now. The final step I need is stepping up that 5v output to 12v.

Ahhh I didn't read everything posted "Recently" here, but you should put a simple transistor on the PWM output that switches to ground. That's what all the automotive stuff does and the voltage doesn't matter. Flyback diodes and all that other BS are not required unless you're making an motor driver, but that's what is already built into these fans we're talking about. I can make you up a little drawing later if this doesn't make sense.

I get the general gist, but I wouldn't mind a drawing anyways if you're still down. I'd be very happy to KISS and minimize components

Something like this? (Only the Q1 and M1 relate)
Increasing arduino PWM signal voltage from 5V to 10V using a transistor

OErDc.png
 
Cool bump for this thread. I finally put the new radiator in this jeep (been sitting waiting) and committed to the new fan setup. I picked up a handful of these N channel mosfets on Amazon: RFP30N06LE

https://www.sparkfun.com/datasheets/Components/General/RFP30N06LE.pdf

and wired it like this (might have been linked previously in the thread IIRC

How do I convert 0-5v PWM to a 0-12v PWM

EEBMi.png


And the output PWM is now correctly in the 0-12v range the fan needs depending on what duty cycle the code is demanding. I made a mountain out of a molehill with this lol. But at least we got here in the end. Now to test it with the actual fan on the bench, then test it in the jeep, and send it.
 
Fan is mounted in the jeep, but no wiring yet. Tight to the steering box, but fits great. Hoping to wrap that up this afternoon if rain plays nice. Giving it a 100 amp auto circuit breaker, and giving the Arduino its own power straight from the batt to minimize noise.

Something that I read about and just confirmed, the above step-up circuit inverts the signal, because when the trigger wire gets voltage, the mosfet circuit completes its ground. So I had already inverted the signal in code, I need to delete that now :laughing:. But things are looking good. Once I have this tested and proven working, I'll come back with a fresh reply outlining every single component used, wiring diagram, the part file for the case, and the complete working code. Probably edit it into the first post as well, just so it can be helpful to anyone who wants to take the project on themselves.
 
Rather than deleting the code, you could make a constant that allows that inversion code to enabled and disabled.

I ended up leaving the inverted code, adding a stanrdard (non inverted) code, and just commented out the one I’m not using. With a second comment just to articulate how to choose between the two. Quick and dirty, but meh haha.

It’s in the jeep and it works. I’m probably going to broaden the range available on the knob, and I really need to add some smoothing to how frequently the temperature updates. I don’t know if I should make it update like once per second, or have it do some averaging. Averaging is probably better, but open to ideas there. Very happy with how everything is working though, just need to finalize the wiring and do some driving.


 
Top Back Refresh