ESP32-C3 Super Mini Relay Timer
In this project, I will guide you through building a customizable countdown timer using the ESP32-C3 microcontroller, a 0.96-inch OLED display, a rotary encoder, and a relay module. This project is perfect for controlling devices that require timed intervals, such as lamps, fans, or any other appliances.
Components Required
- ESP32-C3 Super Mini (Affiliate): https://s.click.aliexpress.com/e/_DdbQYtJ
- 0.96-inch OLED Display (128×64 pixels) (Affiliate): https://s.click.aliexpress.com/e/_Dm4UE3J
- KY-040 Rotary Encoder (Affiliate): https://s.click.aliexpress.com/e/_DdyBNk1
- Relay Module 1 channel 3.3V red (Affiliate): https://s.click.aliexpress.com/e/_DCtP7d7
- Breadboard and jumper wires (Affiliate): https://s.click.aliexpress.com/e/_Dl5kuk1
Esp32-C3 Super Mini Pinout
Testing: Circuit Diagram and Connections
Here is how you need to wire the components to the ESP32-C3:
Rotary Encoder Pins:
CLK
: Connected to GPIO 2.DT
: Connected to GPIO 3.SW
: Connected to GPIO 1.
OLED Display: Uses I2C communication.
SDA
: Connect to GPIO 8.SCL
: Connect to GPIO 9.
Relay Module:
IN
: Connected to GPIO 0.VCC
andGND
: Connected to 5V(External Power Supply) and ground of the ESP32-C3.
Functionality Overview
- Rotary Encoder:
- The encoder is used to select the timer value (0–60 minutes).
- Turning the encoder adjusts the timer duration in one-minute increments.
- OLED Display:
- Displays the selected timer duration during setup.
- Shows the remaining time once the countdown starts.
- Relay Module:
- When the countdown starts, the relay is triggered to turn on the connected appliance.
- After the timer completes, the relay is turned off.
- Push Button:
- The button (on the rotary encoder) starts the countdown after the time is selected.
- During the countdown, the button is disabled to avoid accidental restarts.
Code Explanation
Let’s break down the code used to make this project work.
#include <U8g2lib.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define I2C_ADDRESS 0x3C U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); const int CLK = 2; const int DT = 3; const int SW = 1; const int RELAY_PIN = 0; int encoderValue = 0; int lastCLKState; int lastDTState; bool buttonPressed = false; bool timerRunning = false; unsigned long countdownStartTime; unsigned long countdownDuration = 0; void setup() { initializePins(); initializeSerial(); initializeDisplay(); } void loop() { handleEncoder(); handleCountdown(); handleButton(); delay(1); } void initializePins() { pinMode(CLK, INPUT); pinMode(DT, INPUT); pinMode(SW, INPUT_PULLUP); pinMode(RELAY_PIN, OUTPUT); lastCLKState = digitalRead(CLK); lastDTState = digitalRead(DT); } void initializeSerial() { Serial.begin(115200); } void initializeDisplay() { u8g2.begin(); updateDisplayTime(); } void handleEncoder() { int currentStateCLK = digitalRead(CLK); int currentStateDT = digitalRead(DT); if (currentStateCLK != lastCLKState && currentStateCLK == HIGH) { encoderValue += (currentStateDT == LOW) ? 1 : -1; encoderValue = constrain(encoderValue, 0, 60); Serial.print("Encoder Value: "); Serial.println(encoderValue); updateDisplayTime(); countdownDuration = encoderValue * 60; } lastCLKState = currentStateCLK; lastDTState = currentStateDT; } void updateDisplayTime() { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_ncenB08_tr); u8g2.setCursor(15, 15); u8g2.print("Select the Time:"); u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.setCursor(45, 35); u8g2.print(encoderValue); u8g2.print(" min"); u8g2.sendBuffer(); } void handleCountdown() { if (timerRunning) { unsigned long elapsedTime = millis() - countdownStartTime; unsigned long remainingTime = (countdownDuration * 1000) - elapsedTime; if (elapsedTime < countdownDuration * 1000) { displayCountdown(remainingTime); } else { endCountdown(); } } } void displayCountdown(unsigned long remainingTime) { int minutes = remainingTime / (60 * 1000); int seconds = (remainingTime % (60 * 1000)) / 1000; Serial.print("Remaining Time: "); Serial.print(minutes); Serial.print(" minutes and "); Serial.print(seconds); Serial.println(" seconds"); u8g2.clearBuffer(); u8g2.setFont(u8g2_font_ncenB08_tr); u8g2.setCursor(15, 15); u8g2.print("Remaining Time: "); u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.setCursor(35, 35); u8g2.print(minutes); u8g2.print("m "); u8g2.print(seconds); u8g2.print("s"); u8g2.sendBuffer(); } void endCountdown() { Serial.println("Countdown complete"); timerRunning = false; Serial.println("Input enabled"); u8g2.clearBuffer(); u8g2.setFont(u8g2_font_ncenB08_tr); u8g2.setCursor(0, 10); u8g2.print("Countdown Complete!"); u8g2.sendBuffer(); digitalWrite(RELAY_PIN, LOW); } void handleButton() { if (digitalRead(SW) == LOW && !buttonPressed && !timerRunning) { startCountdown(); } else if (digitalRead(SW) == HIGH && buttonPressed) { buttonPressed = false; } } void startCountdown() { buttonPressed = true; timerRunning = true; countdownStartTime = millis(); Serial.print("Countdown started for "); Serial.print(countdownDuration / 60); Serial.print(" minutes and "); Serial.print(countdownDuration % 60); Serial.println(" seconds"); digitalWrite(RELAY_PIN, HIGH); }
1. Including the U8g2 Library for OLED Control
The code starts by including the U8g2lib library, which helps in managing the OLED display:
#include <U8g2lib.h>
This library provides functions to draw text and graphics on the screen. The display is initialized with this line:
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
2. Pin Definitions
The ESP32-C3 GPIO pins for the rotary encoder and relay are defined as follows:
const int CLK = 2; const int DT = 3; const int SW = 1; const int RELAY_PIN = 0;
3. Setup Function
During the setup phase, the code initializes the pins, the OLED display, and the serial communication:
void setup() { initializePins(); initializeSerial(); initializeDisplay(); }
The pins are set up using:
void initializePins() { pinMode(CLK, INPUT); pinMode(DT, INPUT); pinMode(SW, INPUT_PULLUP); pinMode(RELAY_PIN, OUTPUT); lastCLKState = digitalRead(CLK); lastDTState = digitalRead(DT); }
4. Handling the Rotary Encoder
The handleEncoder()
function is responsible for reading the rotary encoder’s state and adjusting the timer value accordingly:
void handleEncoder() { int currentStateCLK = digitalRead(CLK); int currentStateDT = digitalRead(DT); if (currentStateCLK != lastCLKState && currentStateCLK == HIGH) { encoderValue += (currentStateDT == LOW) ? 1 : -1; encoderValue = constrain(encoderValue, 0, 60); updateDisplayTime(); countdownDuration = encoderValue * 60; } lastCLKState = currentStateCLK; }
This part ensures that when you rotate the encoder, the time value is updated between 0 and 60 minutes.
5. Updating the Display
The selected time is shown on the OLED using updateDisplayTime()
:
void updateDisplayTime() { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_ncenB08_tr); u8g2.setCursor(15, 15); u8g2.print("Select the Time:"); u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.setCursor(45, 35); u8g2.print(encoderValue); u8g2.print(" min"); u8g2.sendBuffer(); }
6. Starting and Handling the Countdown
Once the button is pressed, the countdown begins:
void handleButton() { if (digitalRead(SW) == LOW && !buttonPressed && !timerRunning) { startCountdown(); } else if (digitalRead(SW) == HIGH && buttonPressed) { buttonPressed = false; } }
When the countdown is running, the display shows the remaining time:
void displayCountdown(unsigned long remainingTime) { int minutes = remainingTime / (60 * 1000); int seconds = (remainingTime % (60 * 1000)) / 1000; u8g2.clearBuffer(); u8g2.setCursor(15, 15); u8g2.print("Remaining Time: "); u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.setCursor(35, 35); u8g2.print(minutes); u8g2.print("m "); u8g2.print(seconds); u8g2.print("s"); u8g2.sendBuffer(); }
Once the countdown is complete, the relay turns off:
void endCountdown() { timerRunning = false; u8g2.clearBuffer(); u8g2.setCursor(0, 10); u8g2.print("Countdown Complete!"); u8g2.sendBuffer(); digitalWrite(RELAY_PIN, LOW); }
Final Thoughts
This simple countdown timer project can be expanded to control various devices with just a few changes. It’s highly customizable and provides hands-on experience working with ESP32-C3, OLED displays, and rotary encoders. You can adapt it to different time ranges or add more features like pause functionality, sound alarms, or even Wi-Fi control.