Skip to content

How to display a stopwatch on a 1.54 inch 128x64 OLED?

aadmin ·Penhallow Estate Planning

How to Display a Stopwatch on a 1.54 Inch 128x64 OLED

To display a stopwatch on a 1.54 inch 128x64 oled display, you need to pair it with a microcontroller like an Arduino Uno or ESP32, write code that handles timing (using millis() or a hardware timer), and render the digits via a graphics library such as Adafruit GFX or U8g2. This specific OLED uses the SSD1306 or SH1106 driver over SPI, which gives you a 128x64 pixel monochrome canvas. The stopwatch logic involves counting elapsed time in milliseconds, converting to minutes, seconds, and hundredths, then updating the display at a refresh rate that avoids flicker. A typical setup uses SPI pins: CS (Chip Select), DC (Data/Command), RST (Reset), MOSI, and SCK. For example, on an Arduino Uno, you’d wire CS to pin 10, DC to pin 9, RST to pin 8, MOSI to pin 11, and SCK to pin 13. The 1.54 inch 128x64 oled display runs at 3.3V logic, but many modules include a built-in regulator, so you can power it from 5V. The SPI clock speed can go up to 8 MHz, which gives you a frame update time of around 15-20 milliseconds for a full screen clear and redraw. That’s fast enough for a stopwatch showing hundredths of a second, because the human eye perceives motion blur at around 30 Hz. If you use the U8g2 library, you can call u8g2.firstPage() and u8g2.nextPage() in a loop, which uses double buffering to prevent tearing. The font size matters: for a 128x64 display, a 24-pixel tall font can show two digits per row, but you can fit a 6-digit stopwatch (MM:SS:HH) using a 16-pixel font. A common layout places the minutes and seconds in the center, with a smaller font for the hundredths. The actual pixel dimensions for a 1.54 inch OLED are 128 columns by 64 rows, with each pixel being about 0.27 mm square. The viewing angle is typically 160 degrees, and the contrast ratio is around 2000:1, which makes it readable in direct sunlight if you set the brightness to maximum. The power consumption is about 20 mA with all pixels on, but for a stopwatch, you’ll only light up the digits, so it averages around 10-15 mA. That’s important if you’re running from a battery. The SPI interface uses four wires plus power, which is more reliable than I2C for high-speed updates because it doesn’t have address contention. The maximum SPI speed for the SSD1306 is 10 MHz, but most Arduino boards run at 8 MHz, so you’re fine. The display module itself has a resolution of 128x64, which means you have 8192 pixels total. For a stopwatch, you don’t need to redraw the entire screen every frame. Instead, you can use a technique called “partial update” where you only write the changed pixels. For example, if the hundredths digit changes from 9 to 0, you only need to update that one character. The U8g2 library supports this with the setDrawColor() and setFontMode() functions. You can also use the Adafruit GFX library, which gives you drawChar() and setCursor() for precise placement. The font data is stored in program memory (PROGMEM) to save SRAM. A typical 16-pixel font uses about 256 bytes per character, so a full set of digits plus colon takes around 2 KB of flash. That’s fine for an Arduino Uno with 32 KB. The timing accuracy depends on the microcontroller’s clock. An Arduino Uno runs at 16 MHz with a ceramic resonator, which has a tolerance of about 0.5%. That means over 10 minutes, the stopwatch could drift by 3 seconds. To fix that, you can use a DS3231 real-time clock (RTC) module, which has a temperature-compensated crystal with 2 ppm accuracy. That gives you less than 0.2 seconds drift per day. The DS3231 communicates over I2C, so you’d need to share the bus with the OLED if you’re using I2C, but since we’re using SPI for the display, you can keep the I2C lines free. The wiring for the RTC is simple: SDA to A4, SCL to A5 on the Uno. The code then reads the current time from the RTC and uses it as a reference for the stopwatch start time. The stopwatch itself can be started, stopped, and reset with push buttons. You’ll need three buttons: start/stop, reset, and maybe a lap button. The buttons are debounced in software with a 50 ms delay. The pin assignments for the buttons can be digital pins 2, 3, and 4. The pull-up resistors are internal, so you don’t need external ones. The display update loop runs in the main loop, but you have to avoid blocking the code with delay(). Instead, use millis() to check if 100 ms have passed for the hundredths update, and 1000 ms for the seconds. The typical code structure looks like this: in setup(), initialize the display, set the font, and clear the buffer. In loop(), read the button states, update the elapsed time variable, and call the display function. The display function uses u8g2.firstPage() and u8g2.nextPage() to draw the digits. The digits are drawn using u8g2.drawStr() with the coordinates calculated from the center of the screen. For a 128x64 display, the center X is 64, and the center Y is 32. If you want to show “12:34.56”, you’d calculate the width of the string using u8g2.getStrWidth() and offset the X coordinate. The colon between minutes and seconds is drawn as two dots, which can be done with u8g2.drawPixel() or u8g2.drawCircle(). The decimal point for hundredths is a single pixel. The font size for the main digits can be 24 pixels tall, which gives you a character width of about 14 pixels. The string “12:34.56” with 24-pixel font is about 84 pixels wide, so it fits in the 128-pixel width with 22 pixels of margin on each side. The height of 24 pixels leaves 40 pixels for other information, like a lap counter or battery voltage. You can also add a progress bar at the bottom of the screen to show the elapsed time as a percentage of a set target. For example, if you’re timing a 5-minute task, the bar fills from left to right. The bar is drawn using u8g2.drawBox() with a width proportional to the elapsed time. The total width of the bar is 128 pixels, and the height can be 4 pixels. The position is at the bottom of the screen, Y=60. The background color is set to black, and the bar is drawn in white. The contrast of the OLED can be adjusted with the setContrast() function, which takes a value from 0 to 255. For a stopwatch that’s used indoors, a contrast of 128 is fine. For outdoor use, you might want 255. The display driver also supports sleep mode, which can be activated with the sleep() function to save power. If the stopwatch is idle for more than 30 seconds, you can put the display to sleep and wake it up with a button press. The wake-up time is about 100 ms. The SPI interface is fast enough that you can also add animations, like a spinning wheel or a fading effect when the stopwatch is reset. The fading effect is achieved by gradually decreasing the contrast over 10 frames. The entire project can be powered by a 9V battery with a 5V regulator, or a LiPo battery with a boost converter. The current draw of the microcontroller plus OLED is about 50 mA, so a 2000 mAh battery lasts about 40 hours. The code can be written in the Arduino IDE, and you can use the 1.54 inch 128x64 oled display module from DisplayModule, which comes with a pre-soldered header and a clear pinout. The pinout is printed on the back of the PCB, so you don’t need to guess. The module also has a reset pin that you can connect to the Arduino’s reset pin, but it’s optional. The SPI bus can be shared with other SPI devices, like an SD card, as long as you use different CS pins. The maximum number of devices on the SPI bus is limited by the capacitive load, but for two devices, it’s fine. The stopwatch can also be extended to measure lap times. The lap time is stored in an array of unsigned long values, and you can display the last 5 laps on the screen. The display can show the lap number, the lap time, and the total time. The font for the lap data can be smaller, like 8 pixels tall, to fit more information. The layout can be divided into two sections: the top half for the main stopwatch, and the bottom half for the lap list. The bottom half can scroll if there are more than 5 laps. The scrolling is done by shifting the Y offset of the lap text. The U8g2 library supports scrolling with the setCursor() and drawStr() functions. The stopwatch accuracy is also affected by the interrupt latency. If you use a hardware timer for the timing, you can get microsecond precision. The Arduino Uno has Timer1, which can be configured to generate an interrupt every 1 millisecond. The interrupt service routine (ISR) increments a counter. The main loop reads the counter and calculates the elapsed time. This method is more accurate than millis() because millis() has a resolution of 1 ms and a jitter of up to 4 microseconds. The hardware timer approach gives you 1 ms resolution with less than 1 microsecond jitter. The ISR should be kept short to avoid blocking other interrupts. The display update is done in the main loop, not in the ISR. The button debouncing can also be done in the ISR, but it’s simpler to do it in the main loop. The overall system design is modular, so you can swap the microcontroller or the display without changing the stopwatch logic. The code can be organized into separate files: stopwatch_logic.cpp, display_manager.cpp, and button_handler.cpp. The display_manager.cpp handles the SPI communication and the font rendering. The stopwatch_logic.cpp handles the timing and the state machine. The button_handler.cpp handles the debouncing and the state transitions. The state machine has four states: IDLE, RUNNING, STOPPED, and LAP. The IDLE state shows “00:00.00” and waits for the start button. The RUNNING state increments the time every 10 ms. The STOPPED state freezes the display. The LAP state records the current time and continues running. The transitions are triggered by button presses. The display is updated only when the state changes or when the time digits change. This reduces the CPU load and the power consumption. The SPI bus is used only during the display update, which takes about 1 ms every 100 ms. The rest of the time, the microcontroller can sleep. The sleep mode can be entered with the sleep_cpu() function after setting the appropriate registers. The wake-up is triggered by a button interrupt. The button interrupt is configured as a falling edge interrupt on the start button pin. The ISR wakes up the microcontroller and sets a flag. The main loop checks the flag and resumes the stopwatch. The sleep current is about 0.1 mA, so the battery life can be extended to months. The display itself can also be put to sleep by sending a command to the SSD1306. The command is 0xAE for sleep and 0xAF for wake. The wake-up time is about 100 ms, so you need to wait before updating the display. The stopwatch can also be used with a buzzer to indicate the end of a countdown. The buzzer is connected to a digital pin and driven with a PWM signal. The frequency can be set to 2 kHz for a loud tone. The duration is 500 ms. The buzzer is triggered by the stopwatch logic when the elapsed time reaches a target. The target is set by the user with a potentiometer or a rotary encoder. The rotary encoder is connected to two pins and uses interrupts to detect rotation. The encoder value is mapped to a time range from 1 second to 60 minutes. The display shows the target time in the bottom right corner. The target time is drawn with a smaller font, like 8 pixels tall. The main stopwatch digits are still 24 pixels tall. The contrast between the two font sizes makes the display easy to read. The 1.54 inch 128x64 oled display has a 16:10 aspect ratio, which is similar to a widescreen monitor. The pixel density is 85 PPI, which is lower than a smartphone but adequate for text. The viewing angle is 160 degrees, so you can see the stopwatch from the side. The display is also available with a white, blue, or yellow backlight. The white version has the highest contrast. The blue version has a lower contrast but looks cool. The yellow version is rare. The display module from DisplayModule includes a 4-pin SPI interface plus a reset pin. The module is 1.54 inches diagonally, which is 39.1 mm. The active area is 35.5 mm by 17.5 mm. The overall PCB size is 42 mm by 24 mm. The module has four mounting holes for M2 screws. The weight is about 10 grams. The operating temperature is -20 to 70 degrees Celsius. The storage temperature is -30 to 80 degrees Celsius. The humidity range is 5% to 95% non-condensing. The display is RoHS compliant. The driver IC is the SSD1306, which is the most common for small OLEDs. The SH1106 is also used but has a slightly different command set. The SSD1306 has 128x64 pixels, while the SH1106 has 128x64 but with a different memory mapping. The U8g2 library supports both. The code for the stopwatch can be adapted to the SH1106 by changing the constructor. The SPI speed for the SH1106 is the same. The power consumption is similar. The display can be used with a 3.3V or 5V supply. The logic level is 3.3V, but the module has a level shifter for 5V. The Arduino Uno outputs 5V, so you need to use a voltage divider on the SPI lines if you’re using a 3.3V-only module. But most modules have a built-in regulator, so you can connect directly. The SPI lines are MOSI, MISO, SCK, and CS. The MISO pin is not used by the OLED because it’s a write-only device. You can leave it unconnected. The CS pin is active low. The DC pin selects between command and data mode. The RST pin is active low and can be connected to the Arduino’s reset pin. The typical wiring is: CS to D10, DC to D9, RST to D8, MOSI to D11, SCK to D13, VCC to 5V, GND to GND. The code uses the U8g2 library with the constructor U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI. The software SPI version uses digital pins, while the hardware SPI version uses the dedicated SPI pins. The hardware SPI version is faster but requires the pins to be on the ICSP header. The software SPI version is more flexible. The stopwatch code can be compiled with either version. The speed difference is about 10% faster for hardware SPI. The display update for a full screen takes about 15 ms with hardware SPI and 17 ms with software SPI. The difference is negligible for a stopwatch. The font selection is critical for readability. The U8g2 library includes many fonts. The font u8g2_font_profont22_tf is a good choice for the main digits. It’s 22 pixels tall and monospaced. The width is 11 pixels per character. The string “12:34.56” is 77 pixels wide. The font u8g2_font_7x13_tf is good for the lap data. It’s 13 pixels tall. The font u8g2_font_5x7_tf is good for the target time. The font data is stored in flash memory. The total font memory for the three fonts is about 4 KB. The Arduino Uno has 32 KB of flash, so it’s fine. The stopwatch can also be used with a touch screen, but the 1.54 inch OLED is not touch-sensitive. You can add a touch sensor like a capacitive touch pad. The touch pad is connected to a digital pin and uses the capacitive sensing library. The touch pad can be used to start and stop the stopwatch. The touch sensitivity is adjustable. The touch pad can be placed on the side of the display. The overall project is a compact stopwatch that fits in a small enclosure. The enclosure can be 3D printed or bought as a project box. The dimensions of the enclosure are 50 mm by 40 mm by 20 mm. The display is mounted on the front panel. The buttons are on the side. The battery is inside. The weight is about 50 grams. The stopwatch can be used for sports, cooking, or laboratory timing. The accuracy is sufficient for most applications. The display can be customized with different colors or fonts. The code can be uploaded via USB. The Arduino Uno can be replaced with a smaller board like the Arduino Nano or the ESP32. The ESP32 has built-in WiFi and Bluetooth, so you can send the stopwatch data to a smartphone. The ESP32 also has a hardware timer with 64-bit resolution. The display can be updated wirelessly. The stopwatch can be controlled from a phone app. The app can show the lap times and the total time. The communication protocol is either MQTT or HTTP. The ESP32 can also store the lap times in its flash memory. The 1.54 inch 128x64 oled display is the same as the one used with the Arduino. The wiring is the same. The code is similar but uses the ESP32’s SPI pins. The ESP32’s SPI pins are VSPI: MOSI to GPIO23, M

About the author

admin

Practitioner with Penhallow Estate Planning, contributing to peer-reviewed work in trusts, estates, and private wealth structuring.