PWM Fan Controller

Used Adafruit’s M0 feather proto board and their OLED feather wing to build a quick PWM fan contoller.  The M0 version was needed for the faster CPU to be able to drive the required PWM frequencies.  To be fair you can probably find a cheaper solution already built on the internet, but where’s the fun in that!

 

Code

/* Module: AdafruitFeatherPWM_rxx.ino
*
* Description: Poor man’s PWM Fan Contoller on Adafruit Feather
* Buttons raise and lower the speed of 4pin PC fan by varying the
* duty cycle of a 24KHz PWM signal
*
* Follow tutorioal on adafruit.com for bringing up feather M0 and OLED display
*
* r01: added looping ramp of fan speed
* r02: added OLED display from Serial-Feather-Screen_r00.ino
* r03: added button functionality to change PWM
*
* TODO:
* Use timers and interrupts
* Add multiple independent PWMs
*
*
* Pin Out
* Pin 10 – connect to PWM pin of fan1, pin 4
* Pin 11 – connect to PWM pin of fan2, pin 4
* GND – splice into the ground signal of fan cable
* provide 12V to fan through power supply or power from motherboard 12V.
* do not connect 12V to feather
* Power feather board from USB
*
* Bill of Material
* (1) Adafruit Feather M0 Basic Proto $19.95
* https://www.adafruit.com/product/2772
* (1) Adafruit FeatherWing OLED – 128×32 OLED $14.95
* https://www.adafruit.com/product/2990
* (1) 4 Pin PWM Fan Extension Cable $6.99 (pack of 5)
* https://www.amazon.com/gp/product/B0788Q4TND/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1
*
*
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#if defined(ESP8266)
#define BUTTON_A 0
#define BUTTON_B 16
#define BUTTON_C 2
#define LED 0
#elif defined(ESP32)
#define BUTTON_A 15
#define BUTTON_B 32
#define BUTTON_C 14
#define LED 13
#elif defined(ARDUINO_STM32F2_FEATHER)
#define BUTTON_A PA15
#define BUTTON_B PC7
#define BUTTON_C PC5
#define LED PB5
#elif defined(TEENSYDUINO)
#define BUTTON_A 4
#define BUTTON_B 3
#define BUTTON_C 8
#define LED 13
#elif defined(ARDUINO_FEATHER52)
#define BUTTON_A 31
#define BUTTON_B 30
#define BUTTON_C 27
#define LED 17
#else // 32u4, M0, M4, and 328p
#define BUTTON_A 9
#define BUTTON_B 6
#define BUTTON_C 5
#define LED 13
#endif

#if (SSD1306_LCDHEIGHT != 32)
#error(“Height incorrect, please fix Adafruit_SSD1306.h!”);
#endif

#define BAUDRATE 115200
#define MAXLINES 4
int linecount = 0;
const int PWM1Pin = 10; // Digital output pin
const int PWM2Pin = 11; // Digital output pin
float dutycycle = 0.7; // Duty cycle of pulses
int period = 30; // two pins 30
int hightime = 0;
int lowtime = 0;
void setup() {
Serial.begin(BAUDRATE);

// Clear the buffer.
display.clearDisplay();
display.display();

pinMode(BUTTON_A, INPUT_PULLUP);
pinMode(BUTTON_B, INPUT_PULLUP);
pinMode(BUTTON_C, INPUT_PULLUP);
pinMode(PWM1Pin, OUTPUT);
pinMode(PWM2Pin, OUTPUT);

Logo();
delay(500);
hightime = int(dutycycle * period);
lowtime = int((1-dutycycle) * period);

display.clearDisplay();
display.display();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println(” FAN SPEED CONTROLER”);
display.println(“\n Duty Cycle: “);
display.println(” Press Button (A,B,C)”);
display.setCursor(85,16);
display.print(dutycycle);
display.display();

}

void loop() {
for (int i = 0; i <=30000; i++) {
digitalWrite(PWM2Pin, HIGH); //
digitalWrite(PWM1Pin, HIGH); //
//hightime = int(35);
delayMicroseconds(hightime);
digitalWrite(PWM1Pin, LOW); //
digitalWrite(PWM2Pin, LOW); //
//lowtime = int(1);
delayMicroseconds(lowtime);
}

if(!digitalRead(BUTTON_A)) {
dutycycle = dutycycle + 0.10;
if (dutycycle > 1.0) {
dutycycle = 1.0;
}
Serial.println(“Button A pressed”);
linecount++;
//delay(200); //pause for debounce
UpdateDisplay();
}
if(!digitalRead(BUTTON_B)) {
dutycycle = 0.60;
Serial.println(“Button B pressed”);
linecount++;
//delay(200); //pause for debounce
UpdateDisplay();
}
if(!digitalRead(BUTTON_C)) {
dutycycle = dutycycle – 0.10;
if (dutycycle < 0.3) {
dutycycle = 0.3;
}
Serial.println(“Button C pressed”);
linecount++;
//delay(200); //pause for debounce
UpdateDisplay();
}

hightime = int(dutycycle * period);
lowtime = int((1-dutycycle) * period);

}

void UpdateDisplay (void)
{
display.setCursor(85,16);
display.fillRect(85,16,30,8,BLACK);
display.display();
display.setCursor(85,16);
display.println(dutycycle);
// display.println(“\nPress Button (A,B,C)”);
display.display();
linecount++;
}

void Logo (void)
{
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println(” PRIMO ROBOTS”);
display.setTextSize(1);
display.println(“”);
display.println(” (c) 2019″);
display.println(“Designed in Los Altos”);
display.setCursor(0,0);
display.display(); // actually display all of the above
delay(1000);
}
// =======================
// END OF LOGO

 

 

About the Author

Jeff Casazza