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:
| Part | Example | Meaning |
|---|---|---|
| Controller and display | SSD1306_128X64_NONAME | Hardware family and resolution |
| Buffer | F, 1, or 2 | Full-screen or page buffer |
| Interface | HW_I2C, SW_I2C, 4W_HW_SPI, etc. | Bus and implementation |
| Arguments | U8G2_R0, reset and bus pins | Rotation and wiring |
For example:
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 betweenclearBuffer()andsendBuffer(). - A constructor containing
_1_or_2_uses a smaller page buffer. Put the complete drawing function inside thefirstPage()/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
- Open lopaka.app and create a project.
- Select U8g2 as the platform.
- Set Screen Size to the display's logical width and height. For an unrotated 128×64 display, use
128 × 64. - 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 layer | Arduino U8g2 output |
|---|---|
| Line | drawLine() |
| Rectangle | drawFrame() / drawBox() |
| Rounded rectangle | drawRFrame() / drawRBox() |
| Ellipse | drawEllipse() / drawFilledEllipse() |
| Triangle | drawTriangle(); always filled in Lopaka's U8g2 inspector |
| Polygon | A helper made from closed drawLine() calls; outline only |
| Text | setFont() plus drawStr() or drawUTF8() |
| Paint, icon, or imported image | A monochrome XBM array plus drawXBM() or drawXBMP() |
| Animation | Frame 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.
| Setting | What it changes for U8g2 |
|---|---|
| Wrapper function | Wraps the active screen in void draw{ScreenName}(void) |
| Declare as PROGMEM | Uses U8X8_PROGMEM and drawXBMP() for Arduino bitmap declarations |
| Include fonts | Includes downloaded custom-font headers; built-in U8g2 fonts need no header |
| Declare images | Emits bitmap arrays used by paint and image layers |
| Animation helpers | Includes downloaded animation headers and emits timing/drawing helpers |
| Declare variables | Emits declarations for layer properties marked as variables |
| Comments, layer titles | Adds layer-name comments to static output |
| Clear/Fill display | Used 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:
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:
- Download every custom
.hfile listed. - Place the headers beside your sketch or generated source.
- 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:
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:
#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:
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:
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 handles | You handle |
|---|---|
| Pixel layout and layer order | Display wiring, voltage, and pin mapping |
| U8g2 drawing calls for supported layers | Exact controller/interface/buffer constructor |
| Built-in font references | Installing U8g2 and the board toolchain |
| Custom-font conversion and downloadable headers | Keeping custom headers with the source |
| One-bit bitmap declarations | RAM/flash budget and buffer choice |
| Optional variables and animation helpers | begin(), refresh loop, timing, and application state |
| A named drawing function per exported screen | Inputs, navigation, sensors, and when to redraw |
Compile, upload, and iterate
- Compile before uploading. Resolve every missing header or symbol first.
- Upload and compare the physical display with the Lopaka canvas.
- Adjust the constructor, rotation, canvas size, or layer positions as needed.
- 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:
// BEGIN LOPAKA GENERATED CODE
// Replace this entire block after exporting again.
// END LOPAKA GENERATED CODETroubleshooting
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 completefirstPage()/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 beforebegin(); 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.