Community project

Dual-Channel Temperature Controller

Photo of Dual-Channel Temperature Controller
Generated with AI

Amir Ashraf

Published November 7, 2026

This dual-channel temperature controller uses an Arduino Uno to monitor ambient temperature with a DHT11 sensor and maintain it within a target range using a relay-controlled cooling system and servo-actuated vent. The guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions that walk through breadboard setup, sensor and display connections, button integration, and relay module configuration.

Following this guide, makers will build a functioning temperature regulation system with a 16x2 LCD display for real-time feedback, three push buttons for control, and automated cooling activation. The firmware handles debounced button input, temperature sensing, relay switching, and servo control to open and close the cooling vent based on user-set temperature targets.

Wiring diagram

Loading diagram…

Gather all the parts

QtyComponent
1

DHT11

Digital temperature and humidity sensor (lower accuracy than DHT22)

1

LCD 16x2 I2C

16x2 character LCD display with I2C backpack

1

Push Button

Momentary push button switch

1

Push Button

Momentary push button switch

1

Push Button

Momentary push button switch

1

Resistor

4.7 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

10 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

10 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

SunFounder 2 Channel 5V Low-Level Trigger Relay Module

5V dual relay module; LOW energizes a channel. Keep factory VCC/JD-VCC jumper installed. Contacts remain disconnected for breadboard testing.

1

SG90 Servo

Micro servo motor (SG90)

1

USB-C 5V Adapter

USB-C wall adapter delivering regulated 5 V to the board's USB or VBUS rail. Default wired power source for desktop / stationary projects.

Assemble it in 7 steps

1. Prepare the breadboard power rails

Unplug USB. Connect Mega 5V to a red rail and GND to a blue rail. Bridge split rails if needed. Keep the relay module beside the breadboard.

  • Never put mains voltage or air-conditioner power wiring on the breadboard. This is a low-voltage control prototype, not a finished AC safety controller.

2. Connect the temperature sensor

Connect DHT11 VCC to 5V, GND to the ground rail, and DATA to Mega D2. Connect the 4.7 kΩ resistor between DATA and 5V. Follow module pin labels, not cable colours. For a bare four-pin DHT11 viewed from the front grille with legs downward, pins left to right are VCC, DATA, unused, GND. Leave the unused pin disconnected. Readings update every two seconds; accuracy is about ±2 C.

3. Connect the four-pin LCD

Use a 16x2 LCD with an I2C backpack: VCC to 5V, GND to ground, SDA to D20 (data), SCL to D21 (clock). Firmware uses address 0x27. Adjust the backpack contrast screw if the text is faint. A bare 16-pin LCD needs a backpack for this wiring.

4. Add the three push buttons

Put each momentary button across the breadboard centre gap. Use contacts on opposite switched sides: one to ground and the other to D22 for ON/OFF, D23 for +, or D24 for -. Legs on the same internally joined side will not work. Internal pull-ups are enabled.

5. Connect the relay control side only

Use the 5V LOW-trigger dual relay module. Leave its factory VCC/JD-VCC jumper installed. VCC goes to 5V, GND to ground, IN1 to D26 (cooling), IN2 to D27 (fan). Put one 10 kΩ resistor from each input to 5V to keep it off during reset. Leave all screw terminals disconnected.

  • Do not connect a compressor directly to these hobby relays. A qualified HVAC technician must design an approved isolated interface and retain the equipment's pressure, overload and other safety controls.

6. Check the low-voltage prototype

Check for shorts between power rails. Power the Mega from USB and the servo from its separate regulated 5V supply for this unloaded test. The controller starts OFF at 24 C. ON/OFF toggles operation; + and - adjust 16–30 C. Fan relay is on while enabled with a healthy sensor. Cooling starts 1 C above target and stops at target, with a three-minute delay after boot and every cooling stop. Sensor failure turns both relays off. Settings reset after power loss.

  • Confirm both relays stay off during reset before considering any external control interface. USB power here is only for the Mega, LCD, sensor and unloaded relay module.

7. Connect and test the cooling vent servo

Use an SG90 micro servo for a lightweight model vent. Signal (usually orange/yellow) goes to Mega D6. Servo red goes to the separate regulated 5V adapter positive output; brown/black goes to adapter ground. Connect adapter ground to Mega GND. Use a supply rated at least 1A with accessible insulated low-voltage output leads. Keep its positive output separate from the Mega 5V rail; never power the servo from the Mega. The servo moves to 90 degrees only while the cooling relay is ON; it returns to 10 degrees when cooling stops, the controller is OFF, or the sensor fails. During the three-minute cooling delay it stays closed.

  • Disconnect the vent linkage for the first test. Fit the horn after checking the closed and open positions; adjust VENT_CLOSED and VENT_OPEN in firmware to avoid mechanical stops. Do not force a real AC damper with this small hobby servo.
  • Keep mains connections enclosed inside the purchased adapter. Never cut or handle mains wiring. Disconnect both supplies before changing connections.

Review all connections

1. Connections between "temperature" and "Arduino"

FunctiontemperatureArduino
powerVCC5V
groundGNDGND
dataDATAGPIO 2

2. Connections between "sensorPullup" and "Arduino"

FunctionsensorPullupArduino
digitalP1 → DHT11 DATAEXT
powerP25V

3. Connections between "lcd" and "Arduino"

FunctionlcdArduino
powerVCC5V
groundGNDGND
i2cSDAGPIO 20
i2cSCLGPIO 21

4. Connections between "powerButton" and "Arduino"

FunctionpowerButtonArduino
digitalSIGNALGPIO 22
groundGNDGND

5. Connections between "plusButton" and "Arduino"

FunctionplusButtonArduino
digitalSIGNALGPIO 23
groundGNDGND

6. Connections between "minusButton" and "Arduino"

FunctionminusButtonArduino
digitalSIGNALGPIO 24
groundGNDGND

7. Connections between "relays" and "Arduino"

FunctionrelaysArduino
powerVCC5V
groundGNDGND
digitalIN1GPIO 26
digitalIN2GPIO 27

8. Connections between "relayPullup1" and "Arduino"

FunctionrelayPullup1Arduino
digitalP1 → SunFounder 2 Channel 5V Low-Level Trigger Relay Module IN1EXT
powerP25V

9. Connections between "relayPullup2" and "Arduino"

FunctionrelayPullup2Arduino
digitalP1 → SunFounder 2 Channel 5V Low-Level Trigger Relay Module IN2EXT
powerP25V

10. Connections between "ventServo" and "Arduino"

FunctionventServoArduino
pwmSIGNALGPIO 6
powerVCC → USB-C 5V Adapter +5VEXT
groundGND → USB-C 5V Adapter GNDEXT

11. Connections between "servoSupply" and "Arduino"

FunctionservoSupplyArduino
groundGNDGND

Deploy the firmware

#include <Arduino.h>
#include <Wire.h>
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#include <math.h>
#include <Servo.h>
Servo vent;
const uint8_t VENT_PIN=6, VENT_CLOSED=10, VENT_OPEN=90;
DHT sensor(2,DHT11);
LiquidCrystal_I2C lcd(0x27,16,2);
const uint8_t COOL=26,FAN=27;
const unsigned long DELAY_MS=180000UL;
bool enabled=false,cooling=false,valid=false;
int target=24;
float temperature=0;
unsigned long stopped=0,requested=0;
char oldLines[2][17]={{0},{0}};
struct Button {
 Button(uint8_t p):pin(p){}
 uint8_t pin; bool raw=HIGH,stable=HIGH; unsigned long changed=0;
 bool pressed(unsigned long now) {
  bool r=digitalRead(pin);
  if(r!=raw){raw=r;changed=now;}
  if(r!=stable && now-changed>=35){stable=r;return r==LOW;}
  return false;
 }
};
Button buttons[3]={{22},{23},{24}};
void line(uint8_t row,const char* text){
 char padded[17]; memset(padded,' ',16); padded[16]=0;
 size_t n=strlen(text); if(n>16)n=16; memcpy(padded,text,n);
 if(strcmp(padded,oldLines[row])){lcd.setCursor(0,row);lcd.print(padded);strcpy(oldLines[row],padded);}
}
void setup(){
 vent.write(VENT_CLOSED);vent.attach(VENT_PIN);
 digitalWrite(COOL,HIGH);digitalWrite(FAN,HIGH);
 pinMode(COOL,OUTPUT);pinMode(FAN,OUTPUT);
 for(auto &b:buttons)pinMode(b.pin,INPUT_PULLUP);
 Wire.begin();lcd.init();lcd.backlight();
 sensor.begin();requested=millis();stopped=millis();
}
void loop(){
 unsigned long now=millis();
 if(buttons[0].pressed(now))enabled=!enabled;
 if(buttons[1].pressed(now) && target<30)target++;
 if(buttons[2].pressed(now) && target>16)target--;
 if(now-requested>=2000){
  float t=sensor.readTemperature();
  valid=isfinite(t) && t>=0 && t<=50;
  if(valid)temperature=t;
  requested=now;
 }
 bool wanted=cooling;
 if(!enabled || !valid)wanted=false;
 else if(cooling && temperature<=target)wanted=false;
 else if(!cooling && temperature>=target+1 && now-stopped>=DELAY_MS)wanted=true;
 if(cooling && !wanted)stopped=now;
 if(cooling!=wanted)vent.write(wanted?VENT_OPEN:VENT_CLOSED);
 cooling=wanted;
 digitalWrite(COOL,cooling?LOW:HIGH);
 digitalWrite(FAN,(enabled && valid)?LOW:HIGH);
 char text[24],number[10];
 if(valid){dtostrf(temperature,4,1,number);snprintf(text,sizeof(text),"T:%sC Set:%d",number,target);}
 else snprintf(text,sizeof(text),"Sensor error");
 line(0,text);
 if(!enabled)line(1,"OFF");
 else if(!valid)line(1,"FAULT: relays OFF");
 else if(cooling)line(1,"COOL ON  FAN ON");
 else if(now-stopped<DELAY_MS){snprintf(text,sizeof(text),"WAIT %lus FAN ON",(DELAY_MS-(now-stopped)+999)/1000);line(1,text);}
 else line(1,"READY    FAN ON");
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace, diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik