Timer with OLED Display +Encoder + Esp8266

In this tutorial, we will guide you through the process of connecting an OLED display and a KY-040 rotary encoder to a Wemos D1 Mini. The objective is to create a countdown timer using the provided Arduino IDE code. The countdown timer will be displayed on the OLED screen, and the timer duration can be set using the rotary encoder.

Components Needed:

  1. ESP8266 Development Board (such as NodeMCU) (Affiliate) – https://s.click.aliexpress.com/e/_DD3JQhj
  2. OLED Display (128×64 pixels) (Affiliate) – https://s.click.aliexpress.com/e/_Dm4UE3J
  3. KY-040 rotary encoder (Affiliate) – https://s.click.aliexpress.com/e/_Dl3uGHB
  4. Breadboard and jumper wires (Affiliate) – https://s.click.aliexpress.com/e/_Dl5kuk1

Wiring the Components:

Connect the components as follows:

  • OLED Display:
    • Connect the SDA pin of the OLED display to the D2 pin on the Wemos D1 Mini.
    • Connect the SCL pin of the OLED display to the D1 pin on the Wemos D1 Mini.
    • Connect the GND pin of the OLED display to the GND pin on the Wemos D1 Mini.
    • Connect the VCC pin of the OLED display to the 3.3V pin on the Wemos D1 Mini.

 

 

  • Wemos D1 Mini:
    • D4: Connect to the SW (Switch) pin of the KY-040 encoder.
    • D6: Connect to the CLK (Clock) pin of the KY-040 encoder.
    • D7: Connect to the DT (Data) pin of the KY-040 encoder.

 

 

Installing Libraries

  1. Open the Arduino IDE on your computer.
  2. Navigate to Sketch > Include Library > Manage Libraries.
  3. In the Library Manager, search for “U8g2” and click the “Install” button to install the library.

 

 

Understanding the Code

Let’s break down the code into sections and explain its functionality:

1. Libraries and Constants

This section includes the necessary libraries, such as U8g2lib for OLED display support. It also defines constants for the screen dimensions and I2C address.

 

#include <U8g2lib.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define I2C_ADDRESS 0x3C  // Replace with the correct I2C address

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);

 

2. Pin Definitions

Here, the pins for the KY-040 rotary encoder are defined. CLK and DT represent the clock and data pins, respectively, while SW corresponds to the switch (button) pin.

 

const int CLK = D6;
const int DT = D7;
const int SW = D4;

 

3. Variables Initialization

These variables store the encoder value, previous states of CLK and DT pins, button press status, countdown status, and timestamps for countdown start and duration.

 

int encoderValue = 0;
int lastCLKState;
int lastDTState;
bool buttonPressed = false;
bool timerRunning = false;
unsigned long countdownStartTime;
unsigned long countdownDuration = 0;

 

4. Setup Function

The setup function initializes the pins, Serial communication, and OLED display. It also sets up the initial display with a message to select the time.

 

void setup() {
  // ... (omitting unrelated setup code)

  // Initialize OLED display
  u8g2.begin();
  u8g2.setFont(u8g2_font_ncenB08_tr);
  u8g2.clearBuffer();
  u8g2.setCursor(15, 15);
  u8g2.print("Select the Time:");
  u8g2.setFont(u8g2_font_ncenB14_tr);
  u8g2.setCursor(55, 35);
  u8g2.print("0 min");
  u8g2.sendBuffer();
}

 

5. Loop Function

The loop function contains the main logic of the program. It checks for changes in the encoder state, updates the OLED display accordingly, handles the countdown logic when the timer is running, and manages the button press.

 

void loop() {
  // ... (omitting unrelated loop code)

  // Encoder logic and OLED display updates

  if (timerRunning) {
    // Countdown logic and OLED display updates
  }

  // Button press handling
}

 

6. Encoder and Display Update Logic

This section of the code checks for changes in the encoder state and updates the encoder value. It also updates the OLED display to reflect the selected time in minutes.

 

int currentStateCLK = digitalRead(CLK);
int currentStateDT = digitalRead(DT);

if (currentStateCLK != lastCLKState) {
  // Encoder rotation logic

  // OLED display update
}

 

7. Countdown Logic and Display Updates

If the countdown is active, this part of the code calculates the remaining time and updates the OLED display to show the countdown progress.

 

if (timerRunning) {
  // Countdown logic

  // OLED display update during countdown
}

 

8. Button Press Handling

This section handles the button press to start the countdown. It ensures that the countdown only starts when the button is pressed and the timer is not already running.

 

if (digitalRead(SW) == LOW && !buttonPressed && !timerRunning) {
  // Button press to start countdown

  // Countdown initiation and timestamp
} else if (digitalRead(SW) == HIGH && buttonPressed) {
  buttonPressed = false;
}

 

Complete Code (Updated):

  • Added 60 min Upper Encoder Limit

 

#include <U8g2lib.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define I2C_ADDRESS 0x3C  // Replace with the correct I2C address

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);

const int CLK = D6;
const int DT = D7;
const int SW = D4;

int encoderValue = 0;
int lastCLKState;
int lastDTState;
bool buttonPressed = false;
bool timerRunning = false;
unsigned long countdownStartTime;
unsigned long countdownDuration = 0;

void setup() {
  pinMode(CLK, INPUT);
  pinMode(DT, INPUT);
  pinMode(SW, INPUT_PULLUP);

  lastCLKState = digitalRead(CLK);
  lastDTState = digitalRead(DT);

  Serial.begin(115200);

  // Initialize OLED display
  u8g2.begin();
  //u8g2.setFont(u8g2_font_ncenB08_tr);
  u8g2.setFont(u8g2_font_ncenB08_tr);
  u8g2.clearBuffer();
  u8g2.setCursor(15, 15);
  u8g2.print("Select the Time:");
  u8g2.setFont(u8g2_font_ncenB14_tr);
  u8g2.setCursor(45, 35);
  u8g2.print("0 min");
  u8g2.sendBuffer();
}

void loop() {
  int currentStateCLK = digitalRead(CLK);
  int currentStateDT = digitalRead(DT);

  if (currentStateCLK != lastCLKState) {
    if (currentStateCLK == HIGH) {
      // On rising edge of CLK
      if (currentStateDT == LOW) {
        // Clockwise rotation
        encoderValue++;
      } else {
        // Counter-clockwise rotation
        encoderValue--;
      }

      // Ensure encoderValue is not negative
      if (encoderValue < 0) {
        encoderValue = 0;
      }

      // Limit encoderValue to 60 minutes
      if (encoderValue > 60) {
        encoderValue = 60;
      }

      Serial.print("Encoder Value: ");
      Serial.println(encoderValue);

      u8g2.setFont(u8g2_font_ncenB08_tr);
      u8g2.clearBuffer();
      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();

      // Convert encoder value to seconds (adjust the scale as needed)
      countdownDuration = encoderValue * 60;
    }
  }

  lastCLKState = currentStateCLK;
  lastDTState = currentStateDT;

  if (timerRunning) {
    unsigned long elapsedTime = millis() - countdownStartTime;

    if (elapsedTime < countdownDuration * 1000) {
      unsigned long remainingTime = (countdownDuration * 1000) - elapsedTime;
      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");

      // Update OLED display during countdown
      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();
    } else {
      Serial.println("Countdown complete");
      timerRunning = false;
      Serial.println("Input enabled");

      // Display a message indicating countdown completion
      u8g2.clearBuffer();
      u8g2.setFont(u8g2_font_ncenB08_tr);
      u8g2.setCursor(0, 10);
      u8g2.print("Countdown Complete!");
      u8g2.sendBuffer();
    }
  }

  if (digitalRead(SW) == LOW && !buttonPressed && !timerRunning) {
    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");
  } else if (digitalRead(SW) == HIGH && buttonPressed) {
    buttonPressed = false;
  }

  delay(1);
}

 

Uploading the Code:

  1. Open the Arduino IDE and install the U8g2 library if you haven’t already (Sketch > Include Library > Manage Libraries).
  2. Select the Wemos D1 Mini board from Tools > Board > Wemos D1 R2 & mini.
  3. Choose the correct COM port from Tools > Port.
  4. Copy and paste the provided code into the Arduino IDE.
  5. Click the “Upload” button to upload the code to your Wemos D1 Mini.

Testing the Countdown Timer:

  1. After uploading, open the Serial Monitor (Tools > Serial Monitor) to view the serial output for debugging.
  2. Set the desired countdown time using the rotary encoder.
  3. Press the button on the encoder to start the countdown.
  4. The OLED display will show the remaining time in minutes and seconds.
  5. Once the countdown completes, a message will be displayed on the OLED screen.

 

 

Conclusion

You have successfully created a countdown timer using a Wemos D1 Mini, OLED display, and KY-040 encoder. Feel free to customize the code and experiment with different features for your specific project.