Skip to main content

Overview

The display service is a global, thread‑safe wrapper around the U8G2 driver for a 128×64 SSD1306 OLED. You use it to render text and graphics from any task without flicker or tearing. A FreeRTOS mutex guarantees exclusive access while you draw; helper functions handle region clearing, partial refresh, clipping, and text caching.
This page documents the firmware‑level developer API used across our devices. For user‑facing display behavior (for example, hiding remaining time or device state screens), see the related pages at the end.

Prerequisites

  • ESP32 with hardware I2C available
  • U8G2 library integrated in your firmware build
  • FreeRTOS (for the display mutex)
  • Pins configured for SDA/SCL in your board’s Pins.h

Hardware configuration

Some U8G2 constructors expect the 8‑bit I2C address. If so, pass 0x3C << 1. Verify this matches your constructor signature.

Layout model

The screen is treated as a grid of 8×8‑pixel cells:
  • Grid: 8 rows × 16 columns
  • Origin: (0,0) at the top‑left
  • Cell size: 8×8 pixels

Regions

  • Cells 0–12: Header text (13 cells = 104 px)
  • Cells 13–15: State icons (3 cells = 24 px)
  • 6 rows tall (48 px)
  • 16 cells wide (128 px)
  • All main content should render here
U8G2 text APIs use the baseline for y. For 8‑pixel‑tall fonts, start the first text line at y = 8, then add the font’s ascent/line height for subsequent lines.

Thread safety

The display service exposes a FreeRTOS mutex. Always lock before drawing and unlock when done.
display_mutex.h
If you fail to release the mutex, all other tasks that need the display will block. Always pair a successful xSemaphoreTake with xSemaphoreGive in a finally‑style path.

Basic usage pattern

example_usage.cpp

Initialization

Call initDisplay() once at startup.
display_init.h
1

Create resources and configure the panel

initDisplay() creates the display mutex, initializes the U8G2 display instance, sets addressing/rotation/contrast, and clears the buffer.
After initialization, the screen should be blank and backlit, and displayMutex must be non‑null.
2

Verify drawing

Render a single line of text in the page area.
sanity_check.cpp

Clearing helpers

Efficiently clear only what you plan to redraw. All helpers use an internal updateDisplayArea() to perform a partial refresh of the affected region.
clear_helpers.h
clearPage() sets a clipping window to keep content from spilling into the header or footer.

Refresh helpers

Call a refresh helper after you modify a region. These perform minimal updates to the display.
refresh_helpers.h

Content helpers

setHeader()

Sets the header text. The service uppercases and caches the last value to avoid redundant redraws.
header.h
If you call setHeader() with the same value, the service skips both drawing and refresh.

setFooter()

Sets left‑ and right‑aligned footer text. Both are uppercased and cached.
footer.h
  • Left string aligns to the left edge of the footer region
  • Right string aligns to the right edge of the footer region

drawWrappedText()

Draw text with automatic word wrapping and optional center alignment.
wrapped_text.h
int
required
Starting x‑coordinate in pixels.
int
required
Starting baseline y‑coordinate in pixels.
String
required
Text to render. Supports literal \n for line breaks.
bool
default:"false"
Center each wrapped line within the page area when true.
int
required
Number of lines drawn.

Common patterns

Draw a complete screen

draw_screen.cpp
This pattern clears only the page area, draws new content, and performs a partial refresh for speed.

Update the header only

update_header.cpp

Draw wrapped, centered text

wrapped_text.cpp

Drawing guidelines

1

Lock the mutex for every drawing sequence

Wrap drawing and refresh in a critical section guarded by displayMutex.
lock_and_draw.cpp
2

Prefer region helpers over full clears

Minimize bus traffic and flicker by using clear*() and refresh*() helpers.
3

Respect boundaries

Keep content within:
  • Header: row 0, cells 0–12
  • Icons: row 0, cells 13–15
  • Page: rows 1–6
  • Footer: row 7, cells 0–14; timeout at cell 15
Use clearPage() to clip page content away from header/footer.
4

Choose readable fonts

Consider the font’s ascent and total line height when computing y offsets between lines.

Performance considerations

  • Partial updates only redraw the regions you changed
  • Text caching avoids unnecessary draw/refresh cycles
  • Clipping keeps drawing inside the intended region and reduces overdraw
  • Mutex‑guarded sequences prevent mid‑frame tearing across tasks

Troubleshooting

  • Confirm initDisplay() is called once
  • Verify the I2C address (try 0x3C and 0x3C << 1 depending on constructor)
  • Ensure SDA/SCL match your board’s Pins.h
  • Use region clears/refreshes instead of full‑buffer clears
  • Batch your drawing into one mutex‑protected block
  • Avoid unnecessary font changes between draw calls
  • Remember U8G2 uses a baseline for y
  • Start the first 8‑px font line at y = 8 or compute from font ascent
  • Always release the mutex in error paths
  • Prefer RAII‑style wrappers or goto cleanup patterns

Dependencies

Hide display time (user feature)

How the UI hides remaining time across device, dashboard, and notifications.

Emergency unlock display flow

Screen flow and consequences of triggering Emergency Unlock on device.

Device support matrix

Supported and planned devices across 2025–2026.