Skip to content

U8g2 Arduino OLED and LCD Display Guide

This guide takes a new U8g2 project from an empty Lopaka canvas to a working Arduino sketch. Lopaka creates the drawing code; you connect and initialize the display, choose how it is buffered, and decide when each screen is shown.

What is U8g2?

U8g2 is a graphics library for monochrome OLED and LCD displays. It supports many controllers, drawing primitives, bitmaps, and a large collection of fonts. Lopaka targets the graphical U8g2 API, not the text-only U8x8 API.

Use the upstream setup tutorial and constructor list to confirm that U8g2 supports your exact display.

Before you start

You need:

  • A microcontroller and a monochrome display supported by U8g2.
  • The display controller name, resolution, and interface: for example, SSD1306, 128×64, hardware I2C.
  • The display datasheet or vendor wiring guide.
  • An Arduino-compatible project that can compile and upload a basic sketch.

WARNING

Lopaka does not detect your hardware. Two modules that look alike can use different controllers, addresses, pinouts, or display offsets.

Step 0: Install and test U8g2

In Arduino IDE, open Tools → Manage Libraries, search for U8g2, and install the library by olikraus. This is the installation method documented by the official U8g2 repository.

For a manual Arduino installation, download the official U8g2 Arduino distribution and add its ZIP through Sketch → Include Library → Add .ZIP Library. Other toolchains can use the same upstream distribution through their library dependency workflow. Prefer a released version unless you specifically need an unreleased upstream change.

Before adding Lopaka code, compile and upload one of U8g2's examples for your display. A successful example proves that the constructor, wiring, and library installation are correct.

Step 1: Choose the display constructor

The constructor name selects four important things:

PartExampleMeaning
Controller and displaySSD1306_128X64_NONAMEHardware family and resolution
BufferF, 1, or 2Full-screen or page buffer
InterfaceHW_I2C, SW_I2C, 4W_HW_SPI, etc.Bus and implementation
ArgumentsU8G2_R0, reset and bus pinsRotation and wiring

For example:

cpp
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(
    U8G2_R0,
    U8X8_PIN_NONE
);

This is only a representative constructor for one 128×64 SSD1306 hardware-I2C display. Replace it with the exact constructor and arguments from the official C++ setup list.

Full buffer or page buffer?

  • A constructor containing _F_ keeps the whole frame in RAM. Draw between clearBuffer() and sendBuffer().
  • A constructor containing _1_ or _2_ uses a smaller page buffer. Put the complete drawing function inside the firstPage() / nextPage() picture loop.

Full buffer mode is simpler and faster, but uses more RAM. For a 128×64 one-bit image, the pixel buffer alone is 1,024 bytes. Page mode uses less RAM but redraws the same picture for every page. See U8g2's buffer mode guide.

Step 2: Create the Lopaka project

  1. Open lopaka.app and create a project.
  2. Select U8g2 as the platform.
  3. Set Screen Size to the display's logical width and height. For an unrotated 128×64 display, use 128 × 64.
  4. Keep the default black background. U8g2 output is one bit per pixel, so Lopaka designs are foreground versus background rather than RGB color.

The Lopaka canvas size sets the design surface and coordinate reference; it does not choose a U8g2 constructor or configure the physical display.

Step 3: Design a screen

Use the editor to place text, images, and drawing primitives. Position (0, 0) is the top-left of the canvas. Rename layers before export: names become comments, image identifiers, animation headers, and optional variable names.

U8g2 projects in Lopaka support these emitted concepts:

Lopaka layerArduino U8g2 output
LinedrawLine()
RectangledrawFrame() / drawBox()
Rounded rectangledrawRFrame() / drawRBox()
EllipsedrawEllipse() / drawFilledEllipse()
TriangledrawTriangle(); always filled in Lopaka's U8g2 inspector
PolygonA helper made from closed drawLine() calls; outline only
TextsetFont() plus drawStr() or drawUTF8()
Paint, icon, or imported imageA monochrome XBM array plus drawXBM() or drawXBMP()
AnimationFrame data/header plus frame-selection and drawing helpers

Foreground, background, and inversion

The U8g2 project is monochrome, so the Inspector does not show an RGB color picker. Normal layers set pixels; inverted layers use U8g2 draw color 2 (XOR). Lopaka also emits transparent font and bitmap modes so empty pixels in text and images do not erase layers underneath.

U8g2 notes that XOR is not supported by every primitive, including ellipse procedures. Check inverted shapes on the real display; see setDrawColor.

Position and text baseline

Shape coordinates use their normal U8g2 origins. Text uses the font baseline, not the top edge of the visible letters. A font change can therefore change the apparent vertical alignment even when the text layer keeps the same position.

Text can be rotated by 0°, 90°, 180°, or 270°. Lopaka emits setFontDirection() when needed. See the U8g2 API reference for coordinate details.

Step 4: Configure generated code

Open Code Settings and choose Arduino (Cpp) for an Arduino sketch. U8g2 also has an ESP-IDF (C) syntax option for projects that already use U8g2's C interface; it is not a complete ESP-IDF application or hardware port.

SettingWhat it changes for U8g2
Wrapper functionWraps the active screen in void draw{ScreenName}(void)
Declare as PROGMEMUses U8X8_PROGMEM and drawXBMP() for Arduino bitmap declarations
Include fontsIncludes downloaded custom-font headers; built-in U8g2 fonts need no header
Declare imagesEmits bitmap arrays used by paint and image layers
Animation helpersIncludes downloaded animation headers and emits timing/drawing helpers
Declare variablesEmits declarations for layer properties marked as variables
Comments, layer titlesAdds layer-name comments to static output
Clear/Fill displayUsed by the ESP-IDF C template; the Arduino refresh loop below performs its own clear

Keep all features enabled.

Step 5: Export fonts and images

Built-in fonts

Lopaka knows U8g2's built-in font list and adapts the generated code to the selected font. When a font is built into U8g2, Lopaka emits its library symbol directly and does not generate or offer a separate font header for download. For example:

cpp
u8g2.setFont(u8g2_font_6x10_tr);

For a font that is not on the built-in list, Lopaka treats it as a custom font, emits one matching header include, and lists that header under Code Settings → Fonts for download.

Built-in font sizes are fixed; select a different font to make text larger or smaller. Use the official U8g2 font list to inspect character coverage.

Custom fonts

U8g2 projects accept BDF fonts and can import TTF, OTF, or WOFF through Lopaka's fixed-size font wizard. In Code Settings → Fonts:

  1. Download every custom .h file listed.
  2. Place the headers beside your sketch or generated source.
  3. Keep Include fonts enabled so the generated #include "Font_Name.h" lines remain present.

Only render glyphs used makes a smaller header containing characters currently used by matching text layers. If a text value is a runtime variable, Lopaka exports the full custom font because it cannot predict future characters.

Images and animations

Static images are converted to one-bit XBM data and can be declared directly in the copied source. With Declare as PROGMEM enabled, Lopaka uses U8X8_PROGMEM and drawXBMP(), matching U8g2's PROGMEM bitmap guidance.

Animations are also monochrome. Download every .h file listed under Animations, place it with the sketch, and keep the generated include and helper code. The Arduino helper chooses frames with millis(). You are still responsible for calling the update helper and redrawing the complete screen through the correct buffer loop.

WARNING

Animation output can include starter setup() and loop() code. Merge its includes, frame update, and draw helpers into your sketch, then use the full-buffer or page-buffer refresh pattern from this guide. Do not keep a second setup() or loop().

Step 6: Copy and integrate the code

Create a screen named Main, add a text layer and a frame, then click Copy in Lopaka's code panel. A representative generated drawing function looks like this:

cpp
void drawMain(void) {
    u8g2.setFontMode(1);
    u8g2.setBitmapMode(1);

    u8g2.setFont(u8g2_font_6x10_tr);
    u8g2.drawStr(8, 18, "Hello!");
    u8g2.drawFrame(0, 0, 128, 64);
}

Your exact code will use your screen name, layers, fonts, and coordinates.

Minimal full-buffer sketch

Use this pattern only with an _F_ constructor:

cpp
#include <Arduino.h>
#include <Wire.h>
#include <U8g2lib.h>

// Hardware-specific: replace with the constructor for your display.
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(
    U8G2_R0,
    U8X8_PIN_NONE
);

// Paste Lopaka asset declarations, custom-font includes, helpers,
// and the generated drawMain() function here.
void drawMain(void) {
    u8g2.setFontMode(1);
    u8g2.setBitmapMode(1);
    u8g2.setFont(u8g2_font_6x10_tr);
    u8g2.drawStr(8, 18, "Hello!");
    u8g2.drawFrame(0, 0, 128, 64);
}

void setup(void) {
    u8g2.begin();
}

void loop(void) {
    u8g2.clearBuffer();
    drawMain();
    u8g2.sendBuffer();
}

Page-buffer alternative

Use this picture-loop pattern with a _1_ or _2_ constructor:

cpp
void loop(void) {
    u8g2.firstPage();
    do {
        drawMain();
    } while (u8g2.nextPage());
}

Do not update UI state inside drawMain() in page mode. U8g2 calls it more than once to build one frame, and each pass must draw the same picture. Update sensor values, navigation state, and animation frame selection before firstPage().

Step 7: Add runtime data and more screens

To make a value dynamic, select its property in the Inspector, enable its variable toggle, and keep Declare variables enabled. Lopaka then replaces the literal with a named declaration based on the layer name. Your application can update that value before the next redraw.

Each Lopaka screen exports separately, and its title becomes the drawing function name. Copy each screen's function into the project, merge shared includes and assets so they are declared only once, then choose which function to call:

cpp
enum Screen { MAIN, DETAILS };
Screen currentScreen = MAIN;

void drawCurrentScreen(void) {
    if (currentScreen == MAIN) {
        drawMain();
    } else {
        drawDetails();
    }
}

Lopaka does not generate screen navigation, button handling, menus, sensor reads, or application state.

Lopaka handles vs. you handle

Lopaka handlesYou handle
Pixel layout and layer orderDisplay wiring, voltage, and pin mapping
U8g2 drawing calls for supported layersExact controller/interface/buffer constructor
Built-in font referencesInstalling U8g2 and the board toolchain
Custom-font conversion and downloadable headersKeeping custom headers with the source
One-bit bitmap declarationsRAM/flash budget and buffer choice
Optional variables and animation helpersbegin(), refresh loop, timing, and application state
A named drawing function per exported screenInputs, navigation, sensors, and when to redraw

Compile, upload, and iterate

  1. Compile before uploading. Resolve every missing header or symbol first.
  2. Upload and compare the physical display with the Lopaka canvas.
  3. Adjust the constructor, rotation, canvas size, or layer positions as needed.
  4. When the design changes, replace the complete generated section instead of editing individual generated calls in several places.

Keep hardware and application code outside the generated section so a fresh export cannot overwrite it:

cpp
// BEGIN LOPAKA GENERATED CODE
// Replace this entire block after exporting again.
// END LOPAKA GENERATED CODE

Troubleshooting

The display is blank

  • First upload an official U8g2 example using the same constructor. If it is also blank, check power, ground, wiring, controller, reset pin, and interface.
  • Confirm that u8g2.begin() runs once.
  • Confirm that drawing happens between clearBuffer() / sendBuffer() for _F_, or inside the complete firstPage() / nextPage() loop for _1_ and _2_.
  • For I2C, scan the bus and compare the result with the module documentation. If you call setI2CAddress(), U8g2 expects the address multiplied by two and the call must occur before begin(); see the official reference.

The image is shifted, clipped, mirrored, or rotated

Check that the Lopaka canvas matches the constructor's logical display size. Select the exact controller/display variant: some modules have controller RAM wider than the visible panel. Use U8G2_R0 through U8G2_R3 in the constructor for display rotation, then keep the Lopaka dimensions consistent with that orientation.

Colors or overlapping shapes look inverted

U8g2 output is one bit, not grayscale or RGB. Disable inverted on the layer if XOR was unintended. XOR is not supported by every U8g2 primitive, so inverted ellipses should be verified on hardware.

A font or symbol is undefined

  • Built-in names must exist in the installed U8g2 release and contain the glyphs you use.
  • For a custom font, download its .h, keep it beside the source, and keep Include fonts enabled.
  • If characters are blank, disable Only render glyphs used and export the custom font again, especially when text changes at runtime.

The sketch runs out of memory

Try an _1_ or _2_ page-buffer constructor, smaller images, fewer animation frames, or custom fonts containing only required glyphs. Built-in fonts and assets consume flash; the full display buffer consumes RAM.

Images look rough or reversed

U8g2 images are one-bit, so resize, crop, contrast, threshold, and dithering choices matter. Reopen Lopaka's image editor and preview at the final pixel size. Keep Declare as PROGMEM consistent with the generated declaration and drawXBMP() call.

Page mode flickers or shows only part of the screen

Call the complete drawing function inside the do / while picture loop. Do not draw some layers before firstPage(), change values between pages, or call only firstPage() without nextPage().

FAQ

Does Lopaka configure my SSD1306 or SH1106 display?

No. Lopaka generates drawing code. You choose the matching constructor, bus, pins, address, rotation, buffer mode, and initialization.

Can I use U8x8 instead?

Not with Lopaka's U8g2 output. U8x8 is a separate, character-only API and does not support the graphics primitives Lopaka emits.

Can Lopaka generate color U8g2 code?

No. Lopaka's U8g2 platform is monochrome. Choose a color platform such as TFT_eSPI or ArduinoGFX for an RGB display.

Is the ESP-IDF (C) export a complete firmware project?

No. It emits C-interface drawing code and assets. Your ESP-IDF project must configure the U8g2 structure, GPIO/bus callbacks, task loop, initialization, and platform integration.

Where should I learn more?