Temperature based fan speed control using Arduino & L298N Motor Driver

In this Arduino Project we are going to learn how to create a Temperature based fan speed controller using Arduino, DHT11 and L298N motor driver. Previously we made the similar project using LM35 temperature sensor but it needs multiple electronic components like MOSFET, diodes, and resistor which are little uncomfortable for easy DIY projects. So, here we got L298N motor driver which has everything built in to handle and safely operate the speed of the fan motor. We also shifted the temperature sensor from LM35 to DHT11 module as it looks good in Accessibility, you can also use DHT22 temperature sensor for more accuracy. And the 16×2 LCD with I2C interface provides a clean, real-time readout of temperature and fan speed percentage with minimal wiring overhead.

What makes this build different? Unlike transistor-based fan controllers, the L298N provides a dedicated, thermally protected motor drive path with built-in flyback diode protection. This means fewer external components, better current handling, and a more reliable circuit — especially when driving larger or higher-current fans.

Working principle

This project works under the principle of Pulse width Modulation (PWM). In easy words Pulse Width Modulation (PWM) is a highly efficient technique for controlling analog devices (motors, LEDs, heaters) using a digital source by switching power on and off rapidly. It works by varying the duty cycle.

In our Project :

  1. DHT11 temperature sensor measures the ambient temperature around it and send the temperature data to the Arduino.
  2. Then Arduino microcontroller takes the temperature reading from the DHT11 and runs it through program logic to convert into PWM values. For example, if the temperature is below 25°C, the fan remains off. As the temperature rises above 25°C, the Arduino calculates a proportional PWM value and send it to the motor driver.
  3. PWM works by turning the motor on and off very quickly (thousands of times a second). By changing the ratio of “on” time to “off” time (duty cycle), we can control the average voltage delivered to the fan, thereby controlling its speed. The Arduino sends this PWM signal to the L298N motor driver.
  4. Simultaneously, the Arduino sends the temperature data and the calculated fan speed (as a percentage) to the I2C LCD display for real-time monitoring.

Required Components

To build this project, you will need the following hardware:

  • Arduino Nano (x1)
  • DHT11 Temperature & Humidity Sensor (x1)
  • L298N Motor Driver Module (x1)
  • 16×2 LCD Display with I2C Module (x1)
  • 5V DC Fan (or 12V depending on your power supply) (x1)
  • External Power Supply (7V – 12V) for the motor driver

Understanding the DHT11 Sensor

The DHT11 is a capacitive humidity and thermistor-based temperature sensor housed in a compact 4-pin package. What makes it stand out from purely analog sensors like the LM35 is that it contains an onboard microcontroller that handles all the analog-to-digital conversion internally. The result is that the Arduino receives a clean, pre-calibrated digital signal representing both temperature and relative humidity — with no math or ADC wrangling required on your end.

DHT11 temperature and humidity sensor with pinout

DHT11 Key Specifications

Parameter Specification
Temperature Range 0°C to 50°C
Temperature Accuracy ±2°C
Humidity Range 20% to 90% RH
Humidity Accuracy ±5% RH
Output Type Digital (single-wire serial)
Operating Voltage 3.3V – 5V DC
Sampling Rate 1 reading per 2 seconds (max)
Resolution 1°C temperature / 1% humidity

Communication happens over a single data line using a custom proprietary 1-wire protocol. The DHT11 transmits 40 bits total — 8 bits for integer humidity, 8 bits for decimal humidity, 8 bits for integer temperature, 8 bits for decimal temperature, and 8 bits as a checksum for error detection. The DHT library (by Adafruit or Rob Tillart) handles all of this protocol complexity for you, so in your Arduino code you simply call dht.readTemperature() to get a float value in Celsius.

Module vs. bare sensor: Always prefer the DHT11 module (the blue PCB version) over the bare 4-pin sensor. The module has the required 10kΩ pull-up resistor and a decoupling capacitor already soldered on, saving you two components and potential headaches.

L298N Motor Driver

You might wonder, “Why not just connect the fan directly to the Arduino?” An Arduino pin can only provide a maximum of 40mA of current. A DC fan requires much more current (often 200mA to 500mA). If you connect the fan directly to the Arduino, you will burn the board.

L298N Motor Driver

L298N is a popular integrated circuit that can drive two DC motors (bidirectional, up to 35 V / 2 A per channel) or one stepper motor. It contains two H‑bridges, allowing independent speed (via PWM) and direction control. Typically mounted on a breakout board with onboard 5 V regulator and logic inputs, it interfaces easily with microcontrollers (Arduino, Raspberry Pi). Common in robotics and mechatronics projects (e.g., robot cars, conveyor belts, small actuators). It can handle up to 2A per channel and features built-in PWM control pins. We will use its 12V terminal to power the fan and its EN (Enable) pin to receive the PWM signal from the Arduino.

L298N module pin reference table

Remove the ENA jumper! The L298N module ships with a jumper cap on the ENA pin that connects it to 5V, forcing the motor to run at full speed always. For variable speed control, you must remove this jumper and connect the ENA pin to an Arduino PWM pin (pin 9 or 10).

PWM Fan Speed Control — The Core Concept

Pulse Width Modulation is the technique that makes variable fan speed possible with a digital microcontroller. Instead of varying a continuous voltage level (which would require a DAC), the Arduino rapidly switches its output pin between 5V and 0V at a fixed frequency. The critical variable is the duty cycle — the percentage of each cycle during which the output stays HIGH.

A 25% duty cycle means the pin is HIGH for one quarter of each cycle — the fan motor experiences an average effective voltage of roughly 25% of the supply voltage and spins slowly. At 100% duty cycle, the motor receives full voltage and spins at maximum speed. Arduino’s analogWrite(pin, value) function generates PWM on supported pins, where a value of 0 is 0% and 255 is 100%.

The L298N’s ENA (Enable A) pin accepts this PWM signal directly. When ENA is HIGH, Motor A runs; when ENA is LOW, it stops. By applying a PWM signal to ENA rather than a steady HIGH, you get smooth, proportional speed control that maps perfectly to temperature — the hotter the room, the higher the duty cycle, the faster the fan.

Circuit Connections

The circuit connects four modules to the Arduino: the DHT11 sensor, the L298N motor driver (with fan attached), and the 16×2 I2C LCD display. Here is the complete wiring:

temperature based fan speed controller circuit diagram

 

DHT11 Sensor → Arduino UNO

DHT11 Module Pin Arduino Pin Notes
VCC (+) 5V Sensor power supply
DATA (S) Digital Pin 2 1-wire digital data line
GND (−) GND Common ground

L298N Motor Driver → Arduino UNO

L298N Pin Arduino Pin Notes
ENA PWM Pin 9 Remove the ENA jumper first! PWM speed control
IN1 Digital Pin 7 Direction control — set HIGH for forward spin
IN2 Digital Pin 8 Direction control — set LOW for forward spin
GND GND Shared common ground with Arduino
+12V (VS) External 12V supply (+) Fan motor power supply
+5V (VSS) Do NOT connect Leave open (onboard regulator in use)

DC Fan → L298N Motor Outputs

Fan Wire L298N Terminal Notes
Fan (+) Red OUT1 Connect to Motor A output terminal 1
Fan (−) Black OUT2 Connect to Motor A output terminal 2

16×2 I2C LCD Display → Arduino UNO

LCD (I2C) Pin Arduino Pin Notes
VCC 5V LCD and backlight power
GND GND Common ground
SDA Analog Pin A4 I2C data — fixed pin on UNO
SCL Analog Pin A5 I2C clock — fixed pin on UNO

Build an Intelligent Temperature-Controlled Fan with PCBWay—Custom PCBs & 3D-Printed Enclosures for Your Arduino Project

Create a smart, energy-efficient cooling system that automatically adjusts fan speed based on temperature readings. PCBWay’s integrated manufacturing services help you transform your Arduino, L298N motor driver, and temperature sensor into a polished, professional-grade device.

Custom PCB Manufacturing for Motor Control Precision:
PCBWay manufactures high-quality custom PCBs that consolidate your Arduino, L298N H-bridge driver, temperature sensor interface, and power regulation onto a single, compact board. Our PCBs feature clean power traces to handle motor current spikes, proper signal isolation for noise-free sensor readings, and heat-dissipating copper pours for reliable PWM speed control. Choose our professional SMT assembly to receive a fully populated, tested board ready for deployment.

Ventilated 3D-Printed Enclosures:
Protect your electronics while maintaining airflow with a custom enclosure from PCBWay’s advanced 3D printing service. Design a durable housing using heat-resistant ABS or PETG, incorporating ventilation slots, fan mounting points, and cable management channels. Add a display window for real-time temperature readouts and secure PCB mounting bosses—all printed with precision to ensure perfect fit and function.

Why PCBWay Delivers Professional Results:

  • Stable Motor Control: Robust PCB design handles motor currents without voltage drops
  • Thermal Management: Custom enclosures promote airflow while protecting sensitive electronics
  • Seamless Integration: Perfect alignment between PCB and enclosure from one trusted source
  • Scalable Production: From prototype to multiple units with consistent quality

Upload your PCB design and enclosure model for instant quotes, DFM feedback, and professional manufacturing at PCBWAY.COM

Complete Arduino Code with Explanation

The code below is a complete, production-ready Arduino sketch for this project. It handles sensor reading, PWM speed control via the L298N, LCD display updates with temperature and fan speed percentage, and serial monitor output for debugging. Read through the inline comments — they explain every important line.

Required Libraries — How to Install

Open Arduino IDE, go to Sketch → Include Library → Manage Libraries, then search for and install both of these:

Library Name Author Purpose
DHT sensor library Adafruit Reading temperature & humidity from DHT11/DHT22
LiquidCrystal_I2C Frank de Brabander Driving 16×2 LCD via I2C (PCF8574 backpack)
Wire Arduino (built-in) I2C protocol — pre-installed, no action needed

Temperature based fan speed controller Program code

// ════════════════════════════════════════════════════════════════
//  Temperature Based Fan Speed Controller Circuitschools.com
//  Components : Arduino UNO + DHT11 + L298N + 16x2 I2C LCD
//  Fan Control : L298N ENA (PWM) + IN1/IN2 for direction
//  Display     : Temperature (°C) + Fan Speed (%)
// ════════════════════════════════════════════════════════════════

#include <Wire.h>                 // I2C communication
#include <LiquidCrystal_I2C.h>   // I2C LCD library
#include "DHT.h"                 // DHT11 sensor library (Adafruit)

// ── Pin Definitions ────────────────────────────────────────────
#define DHT_PIN    2    // DHT11 data pin
#define DHT_TYPE   DHT11

#define ENA_PIN    9    // L298N Enable A → PWM fan speed (REMOVE ENA JUMPER!)
#define IN1_PIN    7    // L298N IN1 → motor direction
#define IN2_PIN    8    // L298N IN2 → motor direction

// ── Object Initialization ───────────────────────────────────────
DHT        dht(DHT_PIN, DHT_TYPE);
LiquidCrystal_I2C lcd(0x27, 16, 2);  // Change 0x27 to 0x3F if needed

// ── Temperature Thresholds (°C) ─────────────────────────────────
// Customize these values to suit your environment
const float TEMP_FAN_OFF  = 25.0;  // Below → fan OFF (0%)
const float TEMP_LOW     = 28.0;  // 28–32°C → LOW speed (40%)
const float TEMP_MED     = 32.0;  // 32–36°C → MEDIUM speed (65%)
const float TEMP_HIGH    = 36.0;  // 36–40°C → HIGH speed (85%)
const float TEMP_MAX     = 40.0;  // Above → FULL speed (100%)

// ── Custom Degree Symbol for LCD ────────────────────────────────
byte degSym[8] = {
  0b00110, 0b01001, 0b01001, 0b00110,
  0b00000, 0b00000, 0b00000, 0b00000
};

// ════════════════════════════════════════════════════════════════
void setup() {
  Serial.begin(9600);

  // Initialize L298N pins
  pinMode(ENA_PIN, OUTPUT);
  pinMode(IN1_PIN, OUTPUT);
  pinMode(IN2_PIN, OUTPUT);

  // Set fan direction: IN1=HIGH, IN2=LOW → forward spin
  digitalWrite(IN1_PIN, HIGH);
  digitalWrite(IN2_PIN, LOW);

  // Start fan stopped
  analogWrite(ENA_PIN, 0);

  // Initialize DHT11
  dht.begin();

  // Initialize LCD
  lcd.init();
  lcd.backlight();
  lcd.createChar(0, degSym);

  // Splash screen
  lcd.setCursor(0, 0); lcd.print(" Smart Fan Ctrl ");
  lcd.setCursor(0, 1); lcd.print(" DHT11 + L298N  ");
  delay(2500);
  lcd.clear();
}

// ── Map temperature to PWM value (0–255) ────────────────────────
int tempToPWM(float temp) {
  if (temp < TEMP_FAN_OFF) return 0;
  if (temp < TEMP_LOW)     return 102;  // 40%
  if (temp < TEMP_MED)     return 166;  // 65%
  if (temp < TEMP_HIGH)    return 217;  // 85%
  return 255;                             // 100%
}

// ── Convert PWM to display percentage ───────────────────────────
int pwmToPercent(int pwm) {
  return map(pwm, 0, 255, 0, 100);
}

// ── Get human-readable speed label ──────────────────────────────
String speedLabel(int pwm) {
  if (pwm ==  0)  return "OFF ";
  if (pwm <= 102) return "LOW ";
  if (pwm <= 166) return "MED ";
  if (pwm <= 217) return "HIGH";
  return "MAX ";
}

// ════════════════════════════════════════════════════════════════
void loop() {

  // DHT11 needs ~2 seconds between reads
  delay(2000);

  float temp = dht.readTemperature();   // Read °C
  float hum  = dht.readHumidity();      // Read % RH (optional use)

  // Guard: if sensor read fails, show error and skip
  if (isnan(temp) || isnan(hum)) {
    lcd.setCursor(0, 0); lcd.print("Sensor Error!   ");
    lcd.setCursor(0, 1); lcd.print("Check DHT11 pin ");
    Serial.println("ERROR: DHT11 read failed");
    return;
  }

  // Calculate fan speed values
  int   pwmVal  = tempToPWM(temp);
  int   pct     = pwmToPercent(pwmVal);
  String lbl    = speedLabel(pwmVal);

  // ── Drive the fan via L298N ENA pin ──────────────────────────
  analogWrite(ENA_PIN, pwmVal);

  // ── Update LCD ───────────────────────────────────────────────
  // Line 1: "Temp: 31.0°C  "
  // Line 2: "Fan:  65% MED "
  lcd.setCursor(0, 0);
  lcd.print("Temp:");
  lcd.print(temp, 1);
  lcd.write(0);          // custom degree symbol
  lcd.print("C    ");      // trailing spaces clear old chars

  lcd.setCursor(0, 1);
  lcd.print("Fan:");
  lcd.print(pct);
  lcd.print("% ");
  lcd.print(lbl);
  lcd.print("   ");        // clear leftover chars

  // ── Serial Monitor Output (for debugging) ───────────────────
  Serial.print("Temp: ");    Serial.print(temp);    Serial.print("°C");
  Serial.print(" | Hum: ");  Serial.print(hum);     Serial.print("%");
  Serial.print(" | PWM: ");  Serial.print(pwmVal);
  Serial.print(" | Fan: ");  Serial.print(pct);     Serial.print("%");
  Serial.print(" (");        Serial.print(lbl);
  Serial.println(")");
}

Temperature vs Fan Speed Reference Table

The table below shows the default mapping between temperature ranges, PWM values, and the corresponding fan speed percentage that will appear on the LCD. All threshold values are constants defined at the top of the sketch, making them easy to customize.

Temperature Range Fan Status PWM Value (0–255) Speed % LCD Display (Line 2)
Below 25°C ● OFF 0 0% Fan:0% OFF
25°C – 28°C ● LOW 102 40% Fan:40% LOW
28°C – 32°C ● MEDIUM 166 65% Fan:65% MED
32°C – 36°C ● HIGH 217 85% Fan:85% HIGH
Above 36°C ● MAXIMUM 255 100% Fan:100% MAX
For smoother, continuous speed control instead of discrete steps, replace the tempToPWM() function’s if/else logic with: return map(constrain((int)temp, 25, 40), 25, 40, 0, 255); This linearly maps any temperature between 25°C and 40°C to a PWM value from 0 to 255 — the fan speed then tracks temperature proportionally in real time.

Output on LCD display

The 16×2 LCD provides a clean two-line dashboard that updates in real time every 2 seconds (dictated by the DHT11’s minimum sampling interval):

LCD Screen Layout

temperature based fan speed controller output on lcd display

Line 1: Displays current temperature from DHT11 in Celsius, with a custom degree symbol character.
Line 2: Displays fan speed as a calculated percentage (mapped from PWM 0-255 → 0-100%) followed by a human-readable label (OFF / LOW / MED / HIGH / MAX).

The percentage value shown is computed using Arduino’s built-in map() function that converts the PWM value (0-255 range) to a 0-100% scale. This means the LCD always shows the effective duty cycle as a percentage, giving the user immediate, intuitive feedback on how hard the fan is working at any given moment.

Real-World Applications

  • Server Room
  • Cooling Home
  • Automation Lab
  • Incubators
  • Greenhouse Control
  • Industrial HVAC
  • Battery Enclosures
  • Automotive Cooling
  • Medical Storage
  • Electronics Enclosures

The combination of humidity sensing from the DHT11 and temperature-based fan control opens up additional possibilities beyond simple cooling. In greenhouse automation, for example, you can use the humidity reading to trigger ventilation fans when moisture levels are too high, even if temperature alone wouldn’t trigger them. In battery storage enclosures, precise thermal management using this system can meaningfully extend cell lifespan by preventing heat buildup during charging cycles.

Troubleshooting Common Problems

Fan Always Runs at Full Speed

The ENA jumper on the L298N is still in place. Remove it — this is the single most common mistake in this project. With the jumper installed, ENA is permanently connected to 5V (full speed), and no PWM signal from Arduino can override it.

LCD Shows Garbled Characters or Nothing

First, adjust the blue contrast potentiometer on the I2C backpack module by turning it slowly with a small screwdriver until text becomes visible. If still blank, run an I2C scanner sketch to find your LCD’s actual address — it may be 0x3F rather than 0x27. Update the address in the LiquidCrystal_I2C lcd(0x27, ...) constructor.

LCD Shows “Sensor Error!” on Line 1

The DHT11 read is failing. Check that the DATA wire is connected to Arduino pin 2 (matching the #define DHT_PIN 2 in the code). Ensure you are using the 3-pin module version with the built-in pull-up resistor. If using the bare 4-pin sensor, add a 10kΩ resistor between the DATA pin and 5V.

Fan Doesn’t Spin at Low Speeds

DC motors have a minimum startup voltage below which they won’t turn at all even though PWM is being sent. If the fan won’t spin at 40% duty cycle, increase the minimum PWM value in tempToPWM() from 102 to something higher like 130–150 for the lowest speed tier. This is normal behavior for brushed DC motors.

Temperature Reading Seems Too High or Too Low

The DHT11 has a ±2°C accuracy tolerance, which is normal. If readings seem consistently off by a fixed amount, you can add a calibration offset in the code: float temp = dht.readTemperature() - 1.5; — adjusting the constant to match a reference thermometer reading in your environment.

Power supply grounding: Always connect the negative terminal of your 12V fan power supply to the same GND as the Arduino. Without a common ground, the L298N direction inputs have no voltage reference and the motor will behave erratically or not at all.

Frequently Asked Questions

Why use the L298N instead of a simple transistor for the fan?

The L298N offers several important advantages over a single NPN transistor. It can handle up to 2A of continuous current (vs. ~600mA for a 2N2222), has built-in flyback diode protection for the motor, supports bidirectional control, includes a thermal shutdown safety feature, and eliminates the need for an external back-EMF diode. For fans drawing under 600mA, a transistor works fine, but for any fan drawing more current or for a more robust, professional build, the L298N is the better choice.

Can I use the DHT22 instead of the DHT11?

Yes, and it’s actually recommended for better accuracy. The DHT22 has ±0.5°C accuracy (vs ±2°C for DHT11) and a wider range of −40°C to 80°C. In the code, simply change #define DHT_TYPE DHT11 to #define DHT_TYPE DHT22. All other code, wiring, and library calls remain identical.

Why does the fan buzz at low speeds?

The default PWM frequency on Arduino pin 9 is approximately 490Hz. At low duty cycles, the rapid on/off switching can cause the fan motor’s coils to vibrate audibly — this is the buzz you hear. For quieter operation, you can use an Arduino timer library to increase the PWM frequency to 20kHz or higher (above the range of human hearing), which eliminates the buzzing completely.

What is the voltage drop issue with the L298N?

The L298N uses bipolar junction transistors (BJTs) internally, which have a combined voltage drop of approximately 1.8V to 3.2V across the H-bridge when carrying 1A of current. This means if you supply 12V, your motor only receives about 9–10V. For a 12V fan that needs full voltage for maximum speed, supply 14–15V to compensate. For a 5V fan, supply 7–8V to the VS pin.

How do I add IoT monitoring to this project?

Replace the Arduino UNO with an ESP8266 NodeMCU or ESP32. The wiring remains nearly identical (adjust pin numbers in code). Add the Blynk or MQTT library, create a dashboard on the Blynk app, and stream temperature and fan speed data over Wi-Fi. The ESP32 also has two I2C buses, dual-core processing, and built-in PWM channels — a significant upgrade for IoT deployments.

Can this project control an AC ceiling fan?

Not with this exact circuit — AC fans require a different speed control approach using a TRIAC (phase-angle control) or a dedicated AC fan speed controller module. Working with mains voltage also requires proper isolation, safety enclosures, and ideally certified components. The Arduino can still make the control decisions, but it must interface via an optocoupler to the high-voltage TRIAC stage — never connect Arduino GPIO directly to mains voltage.

Conclusion

Building a temperature based fan speed controller using Arduino, DHT11, and L298N is one of the most practical and well-rounded introductory electronics projects available. It brings together sensor interfacing, PWM motor control, I2C communication, and real-time display — covering four of the most fundamental skills in embedded systems engineering.

The DHT11 delivers reliable digital temperature readings without complex conversion math. The L298N handles the actual motor driving safely and robustly, with built-in protection that simplifies the circuit considerably. And the 16×2 LCD with its live percentage readout turns a functional prototype into something genuinely useful and satisfying to use.

Whether you’re building this for a classroom project, a server room, a greenhouse, or simply to learn how intelligent thermal management works, the skills you pick up here translate directly to real-world engineering. Start assembling today — and don’t forget to remove that ENA jumper.

Add a Comment

Leave a Reply

Your email address will not be published. Required fields are marked *