OLED Timer: Wemos D1 Mini & Buzzer Countdown Project

OLED Timer: Wemos D1 Mini & Buzzer Countdown Project

In this blog post, Iโ€™ll walk you through building a simple minimalistic countdown timer using a Wemos D1 Mini, an OLED display, four buttons, a buzzer, and a BC548 transistor. The project can be used as a kitchen timer, study timer, or any other timed task.

Components Needed

Hereโ€™s a list of components youโ€™ll need to build this project:

  1. Wemos D1 Mini (ESP8266-based board) (Affiliate)https://s.click.aliexpress.com/e/_DD3JQhj
  2. 128×64 OLED Display (I2C) (Affiliate)https://s.click.aliexpress.com/e/_Dm4UE3J
  3. 4 Push Buttons (6x6x12mm) โ€“ https://s.click.aliexpress.com/e/_DdrT1sz
  4. Active Buzzer (Affiliate) โ€“ https://s.click.aliexpress.com/e/_DCb02Wz
  5. BC548 NPN Transistor (Affiliate) https://s.click.aliexpress.com/e/_DFzcuvF
  6. Breadboard and jumper wires (Affiliate)https://s.click.aliexpress.com/e/_Dl5kuk1
  7. 1K Resistor (Affiliate)https://s.click.aliexpress.com/e/_Ddz6nLb

(Optional) 3D Printed enclosure:

  1. Hex Bolt Nut (M2x3x3.2) Heat Insert (Affiliate) –  https://s.click.aliexpress.com/e/_DeqfbBb
  2. 4×6 Cm PCB (Affiliate)https://s.click.aliexpress.com/e/_DDkYF9J
  3. Female headers (Affiliate) https://s.click.aliexpress.com/e/_DdIXIp7

Circuit Diagram

Here’s a description of how to connect each component:

  • OLED Display: Connect the SCL pin to D1 and SDA to D2 on the Wemos D1 Mini. Ensure the I2C address is correct in the code (in this case, 0x3C).
  • Push Buttons: Connect each of the four buttons to digital pins D6, D4, D3, and D5 for increase, decrease, start, and reset, respectively.
  • Buzzer: Connect the positive terminal of the buzzer to the collector of the BC548 transistor (5V -> Buzzer -> Collector), and the emitter to ground. The base of the transistor goes through a current-limiting resistor to D8 on the Wemos.

 

 

 

Code Breakdown

Hereโ€™s the full code used in this project, and Iโ€™ll break down how each part works.

#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 INCREASE_BTN = D6;
const int DECREASE_BTN = D4;
const int START_BTN = D3;
const int RESET_BTN = D5;
const int BUZZER_PIN = D8;  

int timerValue = 0;
bool timerRunning = false;
unsigned long countdownStartTime;
unsigned long countdownDuration = 0;
unsigned long buzzerStartTime = 0;
bool buzzerActive = false;

void setup() {
  pinMode(INCREASE_BTN, INPUT_PULLUP);
  pinMode(DECREASE_BTN, INPUT_PULLUP);
  pinMode(START_BTN, INPUT_PULLUP);
  pinMode(RESET_BTN, INPUT_PULLUP);
  pinMode(BUZZER_PIN, OUTPUT);  
  digitalWrite(BUZZER_PIN, LOW); //Force the pin to be LOW at the start

  Serial.begin(115200);

  // Initialize OLED display
  u8g2.begin();
  u8g2.setFont(u8g2_font_ncenB08_tr);
  updateDisplay();
}

void loop() {
  if (!timerRunning && !buzzerActive) {
    if (digitalRead(INCREASE_BTN) == LOW) {
      timerValue = min(timerValue + 1, 60);
      updateDisplay();
      delay(200);  // Debounce
    }

    if (digitalRead(DECREASE_BTN) == LOW) {
      timerValue = max(timerValue - 1, 0);
      updateDisplay();
      delay(200);  // Debounce
    }

    if (digitalRead(START_BTN) == LOW) {
      timerRunning = true;
      countdownStartTime = millis();
      countdownDuration = timerValue * 60;
      Serial.println("Countdown started for " + String(timerValue) + " minutes");
      delay(200);  // Debounce
    }
  }

  if (digitalRead(RESET_BTN) == LOW) {
    resetTimer();
    delay(200);  // Debounce
  }

  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;

      updateCountdownDisplay(minutes, seconds);
    } else {
      timerComplete();
    }
  }

  if (buzzerActive) {
    if (millis() - buzzerStartTime < 10000) {
      // Buzzer is active for 10 seconds
      digitalWrite(BUZZER_PIN, HIGH);
    } else {
      // Turn off buzzer after 10 seconds
      digitalWrite(BUZZER_PIN, LOW);
      buzzerActive = false;
      updateDisplay();
    }
  }

  delay(1);
}

void updateDisplay() {
  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(timerValue);
  u8g2.print(" min");
  u8g2.sendBuffer();
}

void updateCountdownDisplay(int minutes, int 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 timerComplete() {
  Serial.println("Countdown complete");
  timerRunning = false;
  buzzerActive = true;
  buzzerStartTime = millis();
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_ncenB08_tr);
  u8g2.setCursor(0, 10);
  u8g2.print("Countdown Complete!");
  u8g2.sendBuffer();
}

void resetTimer() {
  timerRunning = false;
  buzzerActive = false;
  digitalWrite(BUZZER_PIN, LOW);
  timerValue = 0;
  updateDisplay();
  Serial.println("Timer reset");
}

How the Code Works

1. OLED Display Setup

The code initializes the OLED display using the U8g2lib library. This library makes it easy to interface with the SSD1306 OLED display, allowing you to show text and simple graphics.

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

The u8g2 object is used throughout the code to update the display when the timer value is changed or during the countdown.

2. Button Inputs

We use four buttons to control the timer:

  • Increase Button (D6): Adds 1 minute to the timer, up to a maximum of 60 minutes.
  • Decrease Button (D4): Decreases the timer value by 1 minute, down to 0.
  • Start Button (D3): Starts the countdown.
  • Reset Button (D5): Resets the timer to 0.

Each button is connected to a digital pin and configured with INPUT_PULLUP, ensuring the default state is HIGH.

3. Timer Logic

The timer logic uses the millis() function to track how much time has passed since the countdown began. The countdown updates the display with the remaining time in minutes and seconds.

if (elapsedTime < countdownDuration * 1000) {
    unsigned long remainingTime = (countdownDuration * 1000) - elapsedTime;
    int minutes = remainingTime / (60 * 1000);
    int seconds = (remainingTime % (60 * 1000)) / 1000;
}

4. Buzzer

Once the countdown reaches zero, the buzzer is activated for 10 seconds using a BC548 transistor to control the signal. Using a transistor like the BC548 is a good solution for connecting an active buzzer to an ESP8266. Here’s why this approach is superior:

  1. Current handling:
    These transistors can easily handle the current required by most active buzzers (typically 20-30mA), which is often more than the ESP8266’s GPIO pins can safely provide directly. the maximum current that can be safely drawn from a single GPIO pin on the Wemos D1 Mini (which uses an ESP8266 microcontroller) is about 12mA. To calculate the base current:
    IB = (VGPIO – VBE) / RB
    IB = (3.3V – 0.7V) / 1000ฮฉ
    IB = 2.6mA           —>        This base current is well within the safe limit for the Wemos D1 Mini’s GPIO pin (max 12mA).
  2. Protection for the ESP8266:
    The transistor acts as a buffer between the ESP8266 and the buzzer, protecting the microcontroller from potential current overloads or voltage spikes.
  3. Flexibility:
    This setup allows you to use buzzers with different voltage requirements (3.3V or 5V) without risking damage to the ESP8266.
  4. Improved performance:
    The transistor can provide a cleaner, more reliable switching action, potentially resulting in better sound quality from the buzzer.
  5. Scalability:
    If you later decide to use a larger or more power-hungry buzzer, this circuit can easily accommodate it without changes to the ESP8266 connection.

The buzzer is turned on with the digitalWrite() function.

if (buzzerActive) {
    if (millis() - buzzerStartTime < 10000) {
        digitalWrite(BUZZER_PIN, HIGH);
    } else {
        digitalWrite(BUZZER_PIN, LOW);
        buzzerActive = false;
    }
}

5. Display Functions

The functions updateDisplay() and updateCountdownDisplay() are used to refresh the OLED screen, showing either the time left or prompting the user to select the time.

(Optional) L-shaped minimalistic enclosure

For those who want to create the enclosure, the following text will explain the basic procedure.

 

The project starts with a 4×6 Cm Protoboard were the components will be placed using a soldering iron and some solder. The way to do this is to built the path of the flow of the electrons on the PCB. The way to do this is up to the creativity of the designer. I placed the components this way and for me is OK. Here are the steps that I took:

Soldering the components

  • First solder the buttons to the board

  • Then the Wemos D1 Mini female headers. This way the microcontroller can be replaced if needed

  • The Oled display should be the last component to be soldered

  • This is the finished back of the board

 

3D Printed Minimalistic Case

*.Stl File for the 3D printed case:

In my case, I used PETG filament for the case, but PLA it should also OK. It takes about 1h30m depending on the 3D printer. In the end there is only one operation to take that is the inserting the M2 brass heat inserts. Then to put it all together the board is secured with 2 hex bolts. The system can be powered directly from the usb port of a computer or from a simple phone charger (5V).

Conclusion

 

 

This simple countdown timer project is a great way to learn about using an OLED display, working with buttons, and controlling a buzzer with an ESP8266-based Wemos D1 Mini. You can customize the timer value or modify the code to suit other timing needs. Whether itโ€™s for a kitchen timer or a reminder for any task, this project is a fun and practical example of what you can do with basic electronics and coding.