#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Pin Definitions
const int pwmPin = 3; // PWM pin for fan speed
const int tachPin = 2; // Tachometer pin to read RPM
const int encoderClk = 6; // Rotary encoder CLK pin
const int encoderDt = 7; // Rotary encoder DT pin
const int encoderSw = 8; // Rotary encoder button
// LCD Setup
LiquidCrystal_I2C lcd(0x27, 16, 2); // LCD address and size (16x2)
// Variables
volatile int tachCount = 0;
unsigned long lastTime = 0;
int fanRPM = 0;
int fanSpeed = 0; // Speed levels: 0 (off) to 3
// Debounce for Encoder
#define DEBOUNCE_DELAY 5
unsigned long lastDebounceTime = 0;
int lastClkState = HIGH;
// Function for loading animation
void loadingAnimation(LiquidCrystal_I2C &lcd) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Loading...");
for (int i = 0; i < 16; i++) { // 16 is the number of columns
lcd.setCursor(i, 1);
lcd.write(byte(255)); // Write a filled block (progress bar)
delay(150); // Adjust speed as needed
}
}
void setup() {
// Pin setup
pinMode(pwmPin, OUTPUT);
pinMode(tachPin, INPUT_PULLUP);
pinMode(encoderClk, INPUT_PULLUP);
pinMode(encoderDt, INPUT_PULLUP);
pinMode(encoderSw, INPUT_PULLUP); // Encoder button with pull-up
analogWrite(pwmPin, 0); // Ensure PWM is off initially
// LCD setup
lcd.init();
lcd.backlight();
// Display loading animation
loadingAnimation(lcd);
// Initial LCD display
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Speed: 0");
lcd.setCursor(0, 1);
lcd.print("RPM: 0");
// Tachometer interrupt
attachInterrupt(digitalPinToInterrupt(tachPin), countTachPulses, FALLING);
}
void loop() {
// Handle rotary encoder input
handleEncoder();
// Calculate RPM every second
if (millis() - lastTime >= 1000) {
noInterrupts();
int count = tachCount;
tachCount = 0;
interrupts();
fanRPM = (count * 60) / 2; // Calculate RPM (2 pulses per revolution)
updateLCD();
lastTime = millis();
}
}
// Handle rotary encoder with debounce
void handleEncoder() {
int clkState = digitalRead(encoderClk);
int dtState = digitalRead(encoderDt);
if (clkState != lastClkState) {
if (millis() - lastDebounceTime > DEBOUNCE_DELAY) {
if (clkState == LOW) { // Detect step on CLK falling edge
if (dtState == HIGH) {
fanSpeed = constrain(fanSpeed + 1, 0, 3); // Clockwise
} else {
fanSpeed = constrain(fanSpeed - 1, 0, 3); // Counter-clockwise
}
updateFanSpeed();
updateLCD();
}
lastDebounceTime = millis();
}
}
lastClkState = clkState;
}
// Update fan speed based on fanSpeed value
void updateFanSpeed() {
int pwmValue = map(fanSpeed, 0, 3, 0, 255); // Map speed to PWM range
analogWrite(pwmPin, pwmValue); // Set PWM speed
}
// Update the LCD display without flickering
void updateLCD() {
static int lastFanSpeed = -1;
static int lastFanRPM = -1;
// Update only if values have changed
if (fanSpeed != lastFanSpeed || fanRPM != lastFanRPM) {
lcd.setCursor(0, 0);
lcd.print("Speed: ");
lcd.print(fanSpeed);
lcd.print(" "); // Clear leftover characters
lcd.setCursor(0, 1);
lcd.print("RPM: ");
lcd.print(fanRPM);
lcd.print(" "); // Clear leftover characters
lastFanSpeed = fanSpeed;
lastFanRPM = fanRPM;
}
}
// Interrupt Service Routine for tachometer
void countTachPulses() {
tachCount++;
}