Skip to content

Instantly share code, notes, and snippets.

@LenweSaralonde
Created December 10, 2025 09:01
Show Gist options
  • Select an option

  • Save LenweSaralonde/7baae7044d0e66c083d5045d0f162324 to your computer and use it in GitHub Desktop.

Select an option

Save LenweSaralonde/7baae7044d0e66c083d5045d0f162324 to your computer and use it in GitHub Desktop.
Simple PWM fan controller for Arduino Nano
#include <EEPROM.h>
// 25 kHz PWM Fan Controller with Button Speed Steps
// Arduino Nano (ATmega328P)
// PWM on D9 (OC1A), Button on D2
// Onboard LED (D13) pulses reflect fan duty
// Last setting is stored in EEPROM
const byte PIN_BUTTON = 2;
const byte PIN_LED = 13; // Onboard LED
const byte PIN_FAN_PWM = 9;
const int LED_PULSE_PERIOD = 500; // Period for the pulsing LED indicator
const int EEPROM_ADDR_STEP = 0; // EEPROM address to store last step
float SPEED_STEPS[] = { 0.0, 0.2, 0.4, 0.6, 0.8, 1.0 };
unsigned long lastLedPeriod = 0;
const int numSteps = sizeof(SPEED_STEPS) / sizeof(SPEED_STEPS[0]);
int currentStep = 0;
bool lastButtonState = HIGH;
unsigned long lastDebounce = 0;
void setup() {
pinMode(PIN_FAN_PWM, OUTPUT); // PWM output
pinMode(PIN_BUTTON, INPUT_PULLUP); // Button input
pinMode(PIN_LED, OUTPUT); // Onboard LED
// Timer1 → 25 kHz PWM on D9
TCCR1A = 0;
TCCR1B = 0;
ICR1 = 639; // TOP → 25 kHz
TCCR1B |= (1 << WGM13) | (1 << WGM12);
TCCR1A |= (1 << WGM11);
TCCR1A |= (1 << COM1A1); // non-inverting mode
TCCR1B |= (1 << CS10); // prescaler = 1
// Load last step from EEPROM
int storedStep = EEPROM.read(EEPROM_ADDR_STEP);
if (storedStep >= 0 && storedStep < numSteps) {
currentStep = storedStep;
} else {
currentStep = 0;
}
}
void loop() {
// ------------------- Button Handling -------------------
bool reading = digitalRead(PIN_BUTTON);
if (reading != lastButtonState) {
if (reading == LOW && lastButtonState == HIGH && millis() - lastDebounce > 50) {
currentStep++;
if (currentStep >= numSteps) {
currentStep = 0;
}
EEPROM.update(EEPROM_ADDR_STEP, currentStep);
}
lastDebounce = millis();
lastButtonState = reading;
}
// ------------------- Apply Fan PWM -------------------
float duty = SPEED_STEPS[currentStep];
OCR1A = (uint16_t)(duty * ICR1);
// ------------------- LED Pulse -------------------
// Scale duty 0.0–1.0 to pulse length of onboard LED
if (millis() - lastLedPeriod >= LED_PULSE_PERIOD * duty) {
digitalWrite(PIN_LED, LOW);
}
if (millis() - lastLedPeriod >= LED_PULSE_PERIOD) {
if (duty > 0) {
digitalWrite(PIN_LED, HIGH);
}
lastLedPeriod = millis();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment