Skip to main content
The display service provides a thread-safe interface for drawing to the 128x64 pixel SSD1306 OLED display. You can access this global singleton from anywhere in the codebase using the U8G2 library.

Hardware Configuration

PropertyValue
Display TypeSSD1306 OLED
Resolution128x64 pixels
InterfaceI2C (Hardware)
RotationU8G2_R0 (0 degrees)
I2C Address0x3C (shifted left by 1 for U8G2)
Contrast255 (maximum)

Display Layout

The display uses a grid system where U8G2 breaks the 128x64 pixel screen into 8x8 pixel cells:
  • Grid Size: 8 cells tall × 16 cells wide
  • Cell Origin: (0,0) at top-left corner
  • Cell Dimensions: 8×8 pixels each
┌─────────────────────────────────────────────────────────┐
│ Header (12 cells)                     │ Icons (4 cells) │  Row 0
├─────────────────────────────────────────────────────────┤
│                                                         │
│                                                         │
│                                                         │
│                    Page Area                            │
│                   (6 rows)                              │
│                                                         │
│                                                         │
├─────────────────────────────────────────────────────────┤
│ Footer (16 cells)                                       │  Row 7
└─────────────────────────────────────────────────────────┘

Display layout showing header, page area, and footer regions

Layout Regions

  • Cells 0-12: Header text (13 cells = 104 pixels)
  • Cells 13-15: State icons (3 cells = 24 pixels)
    • WiFi status
    • Bluetooth status
    • Other system indicators
  • 6 rows of main content
  • 16 cells wide (128 pixels)
  • 48 pixels tall (6 × 8 pixels)
  • Content clips to this area when you use clearPage()

Thread Safety

The display uses a FreeRTOS mutex to ensure thread-safe access from multiple tasks.
display_mutex.h
extern SemaphoreHandle_t displayMutex;
Always acquire the mutex before drawing and release it when finished. Failing to release the mutex will block all other display operations.

Basic Usage Pattern

example_usage.cpp
// Always acquire mutex before drawing
if (xSemaphoreTake(displayMutex, portMAX_DELAY) == pdTRUE) {
    // Perform drawing operations
    display.drawStr(x, y, "Hello World");

    // Always release mutex when done
    xSemaphoreGive(displayMutex);
}

Initialization

Call initDisplay() at startup to initialize the display service:
void initDisplay();
This function:
  1. Creates the display mutex
  2. Initializes the U8G2 display object
  3. Sets the I2C address and display parameters
  4. Clears the display buffer

Clearing Functions

Use clearing functions to efficiently update specific display regions. All clearing functions use updateDisplayArea() for partial updates.
FunctionDescriptionArea Cleared
clearHeader()Clears header text areaCells 0-12, row 0 (preserves icons)
clearIcons()Clears icon areaCells 13-15, row 0 (preserves header)
clearFooter()Clears footer text areaCells 0-14, row 7 (preserves timeout)
clearTimeout()Clears timeout indicatorCell 15, row 7
clearPage()Clears main content areaRows 1-6

clearPage() Parameters

void clearPage(bool includeFooter = false, bool includeHeader = false);
clearPage() sets a clipping window to prevent content from overflowing into the header or footer areas.

Refresh Functions

Refresh functions update specific display regions after you modify them:
FunctionDescriptionArea Updated
refreshHeader()Updates header textCells 3-15, row 0
refreshIcons()Updates icon areaCells 0-2, row 0
refreshFooter()Updates footer areaCells 1-15, row 0
refreshPage()Updates main contentRows 1-6
refreshTimeout()Updates timeout indicatorTimeout cell

refreshPage() Parameters

void refreshPage(bool includeFooter = false, bool includeHeader = false);

Content Functions

setHeader()

Sets the header text, automatically converting to uppercase:
void setHeader(String &text);
The display caches header text to avoid unnecessary redraws. Calling setHeader() with the same text won’t trigger a redraw.

setFooter()

Sets left and right footer text with automatic alignment:
void setFooter(String &left, String &right);
  • Left text aligns to the left edge
  • Right text aligns to the right edge
  • Both texts convert to uppercase automatically
  • Text caching prevents unnecessary redraws

drawWrappedText()

Draws text with automatic word wrapping:
int drawWrappedText(int x, int y, const String &text, bool center = false);
Parameters:
  • x, y: Starting position
  • text: Text to draw (supports literal \n for line breaks)
  • center: Enable center alignment
Returns: Number of lines drawn

Drawing Guidelines

1

Always use mutex protection

Wrap all drawing operations in a mutex lock:
if (xSemaphoreTake(displayMutex, portMAX_DELAY) == pdTRUE) {
    // Drawing operations here
    xSemaphoreGive(displayMutex);
}
2

Use provided clearing functions

Prefer the clearing functions over manual buffer clearing:
clearPage();
display.drawStr(0, 16, "Content");
3

Respect layout boundaries

Keep content within designated areas:
  • Header content: cells 0-12
  • Footer content: cells 0-14
  • Icons: cells 13-15
  • Use clearPage() to prevent content overflow
4

Select appropriate fonts

AreaRecommended Font
Header/Footeru8g2_font_spleen5x8_mu
Page contentChoose based on readability needs
Consider font height when calculating vertical spacing between lines.

Common Patterns

Drawing a Complete Screen

draw_screen.cpp
if (xSemaphoreTake(displayMutex, portMAX_DELAY) == pdTRUE) {
    clearPage();
    display.setFont(u8g2_font_spleen5x8_mu);
    display.drawStr(0, 16, "Main Content");
    refreshPage();
    xSemaphoreGive(displayMutex);
}
This pattern clears the page area, draws new content, and refreshes only the affected region.

Updating Header Only

update_header.cpp
if (xSemaphoreTake(displayMutex, portMAX_DELAY) == pdTRUE) {
    String headerText = "NEW HEADER";
    setHeader(headerText);
    xSemaphoreGive(displayMutex);
}

Drawing Wrapped Text

wrapped_text.cpp
if (xSemaphoreTake(displayMutex, portMAX_DELAY) == pdTRUE) {
    clearPage();
    int lines = drawWrappedText(0, 16, "Long text that will wrap", true);
    refreshPage();
    xSemaphoreGive(displayMutex);
}

Performance Considerations

  • Partial updates: updateDisplayArea() only refreshes specific regions
  • Text caching: Reduces redundant drawing operations
  • Clipping windows: Prevents unnecessary buffer operations
  • Atomic operations: Mutex ensures drawing operations complete without interruption

Memory Management

  • U8G2 library manages the display buffer
  • Text caching prevents unnecessary redraws
  • PROGMEM stores constant strings (following project guidelines)

Dependencies

DependencyPurpose
U8G2 LibraryCore display functionality
FreeRTOSMutex for thread safety
ESP32Hardware I2C interface
Pins.hHardware pin definitions

References