Skip to content

Adafruit GFX ESP32 Arduino Guide

This Adafruit GFX tutorial takes an ESP32 display or Arduino display project from an empty Lopaka canvas to working hardware. Lopaka creates the drawing code and bitmap assets; you choose, wire, and initialize the hardware-specific display library.

What is the Adafruit GFX library?

Adafruit GFX is the common graphics layer used by many display libraries for Arduino-compatible boards, including ESP32. It provides drawing methods for pixels, lines, shapes, text, and bitmaps.

Adafruit GFX does not drive a display by itself. It works with a hardware-specific library such as Adafruit_SSD1306, Adafruit_ST7789, or another driver for the exact controller in your module. The driver handles the bus, initialization, and transfer to the display; GFX supplies the shared drawing API. See Adafruit's library overview for examples of this split.

Before you start

You need:

  • A microcontroller and a display with an Adafruit GFX-compatible driver.
  • The controller name, resolution, interface, and pinout for your module.
  • An Arduino or ESP32 project using the Arduino framework that can compile and upload the driver's example sketch.
  • Enough flash for fonts and bitmap assets, plus enough RAM for any framebuffer used by the driver.

WARNING

Lopaka does not detect your controller or generate wiring and driver configuration. Displays with the same resolution can require different libraries, constructors, addresses, pins, initialization calls, or display offsets.

Step 0: Install and test the display driver

In Arduino IDE, open Tools → Manage Libraries and search for the driver used by your display, such as Adafruit SSD1306 or Adafruit ST7735 and ST7789 Library. Current Arduino IDE versions install declared dependencies such as Adafruit GFX and Adafruit BusIO with the driver. If you install libraries manually, follow the Adafruit GFX repository instructions and the repository for your display driver.

For ESP32, install the Arduino core for ESP32 and select your board. ESP32 sketches use the same setup() and loop() structure shown in this guide.

Before adding Lopaka code, compile and upload an official example for your exact display. For example:

A working vendor example proves that the library, constructor, wiring, address, bus, and initialization are correct. Keep that example as the base sketch and replace only its demo drawing calls with Lopaka output.

Step 1: Choose the Lopaka platform

Lopaka has two Adafruit GFX targets:

Lopaka platformUse it forGenerated objectColor and image output
AdafruitGFX ColorColor TFT, OLED, or matrix driverstftRGB565 colors; one-bit drawBitmap() or RGB565 drawRGBBitmap() images
AdafruitGFX MonoOne-bit OLED or LCD driversdisplay0 and 1 colors; one-bit drawBitmap() images

Choose the target that matches the driver, not just the appearance of the design. A monochrome target does not generate RGB or grayscale pixels. The color target packs colors into the 16-bit RGB565 values used by Adafruit GFX; see Adafruit's coordinate and color model.

The generated object name is part of the integration contract. The simplest approach is to name your driver instance tft for color output or display for monochrome output. Otherwise, rename that identifier in the generated block.

Step 2: Create the Lopaka project

  1. Open lopaka.app and create a project.
  2. Select AdafruitGFX Color or AdafruitGFX Mono.
  3. Set Screen Size to the logical width and height your sketch will use.
  4. For a color project, choose the screen background color. For a monochrome project, design with the fixed black-and-white palette.

Adafruit GFX coordinates start at (0, 0) in the top-left, with X increasing to the right and Y increasing downward. Lopaka uses the same orientation.

If the sketch calls setRotation(), match the Lopaka canvas to the rotated logical dimensions reported by width() and height(). Lopaka does not emit setRotation() or rotate the hardware for you. Adafruit documents the four orientations in Rotating the Display.

Step 3: Design a screen

Place layers with the toolbar, arrange them in the Layers panel, and edit their values in the Inspector. Rename important layers before export: names become comments, bitmap identifiers, polygon helper names, animation headers, and optional variable names.

The current Adafruit GFX generators emit these concepts:

Lopaka layerAdafruit GFX output
LinedrawLine()
RectangledrawRect() / fillRect()
Rounded rectangledrawRoundRect() / fillRoundRect()
CircledrawCircle() / fillCircle()
TriangledrawTriangle() / fillTriangle()
PolygonA helper made from closed drawLine() calls; outline only
TextsetTextColor(), setTextSize(), setFont(), setCursor(), print()
Paint, icon, imagedrawBitmap() or, on the color target, drawRGBBitmap()
AnimationOne-bit frame data plus frame-selection and drawing helpers

Text behavior

The default Adafruit font is the built-in fixed-width 5×7 face. Other GFX fonts use a baseline: the cursor Y coordinate is the line on which the glyphs sit, not the top of the visible letters. Lopaka adjusts the generated Y coordinate for the selected font, but you should still verify alignment on the real display.

The color target exposes a text background color and emits the two-argument setTextColor(foreground, background) form. Adafruit GFX ignores that background argument for custom GFX fonts; it works only with the classic built-in font. To overwrite custom-font text, clear its old bounds with a filled rectangle before drawing it again. The monochrome Lopaka target does not expose text backgrounds. See Adafruit's font behavior and baseline notes.

Step 4: Configure generated code

Open Code Settings. The Adafruit GFX targets have one C++ template and these options:

SettingWhat it changes
Wrapper functionWraps the active screen in void draw{ScreenName}(void)
Include fontsAdds local header includes for non-default GFX fonts
Declare imagesEmits one-bit or RGB565 bitmap arrays used by image layers
Animation helpersAdds animation header includes, timing helpers, and starter setup/loop
Declare variablesEmits declarations for supported layer properties marked as variables
Comments, layer titlesAdds layer-name comments to static drawing output
Clear/Fill displayClears mono output or fills color output at the start of the draw block

Keep Wrapper function, Include fonts, Declare images, and Clear/Fill display enabled for the first render.

The color wrapper starts with tft.fillScreen(...). The monochrome wrapper starts with display.clearDisplay() and ends with display.display() so a buffered display such as SSD1306 is updated. Some other monochrome drivers use a different refresh method; adapt the final call to the official example for your driver.

Step 5: Add fonts, images, and animations

Fonts

The built-in Adafruit 5×7 font needs no header. Lopaka also offers GFX fonts and accepts imported GFX-format font headers. It can convert supported BDF and vector-font imports into GFX format; see the Lopaka fonts guide for the available import paths.

For every non-default font used by the screen:

  1. Open Code Settings → Fonts and download the .h file.
  2. Place the file beside your sketch or generated source.
  3. Keep Include fonts enabled so the generated #include "FontName.h" remains present.

Font data uses program space, and larger fonts use more. Adafruit's font guide explains the included Free family and the GFX font format.

Images

Use the Image tool to import and crop an image at its final pixel size. The output depends on the selected Lopaka target and the image's color mode:

  • Monochrome: a one-bit PROGMEM array drawn with a selected foreground color. Unset pixels are transparent in the generated drawBitmap() call.
  • RGB on AdafruitGFX Color: a uint16_t PROGMEM RGB565 array drawn with drawRGBBitmap().

Keep Declare images enabled to include the arrays in copied code. RGB565 uses two bytes per pixel before compiler and storage overhead, so large full-screen images can consume substantial flash. Lopaka's image import guide covers resize, crop, resampling, dithering, and size estimates. Adafruit documents the underlying bitmap calls in Graphics Primitives.

Animations

Both Lopaka Adafruit targets support frame animations. The exported frames are one-bit bitmaps, including on the color target; the selected layer color is applied when each frame is drawn. This is not full-color video playback.

Download every .h file listed under Code Settings → Animations and keep it with the sketch. The helper chooses a frame using millis().

WARNING

Animation export adds the standard Arduino setup() and loop() entry points, which also work on ESP32 with the Arduino core. The display code inside the generated setup() is only a starter: Adafruit drivers use different initialization calls. Merge the animation includes, update helper, and draw helpers into the working vendor example, retain that driver's constructor and initialization, and keep only one setup() and one loop().

See Animations for the editor workflow and export controls.

Step 6: Copy and integrate the code

Create a screen named Main, add a text layer and an outline rectangle, then click Copy in the code panel. Your exact output will use your layers, colors, fonts, and coordinates.

ST7789 TFT color example

For AdafruitGFX Color, a representative Lopaka function looks like this:

cpp
void drawMain(void) {
    tft.fillScreen(0x0);
    // Greeting
    tft.setTextColor(0xFFFF);
    tft.setCursor(8, 12);
    tft.print("Hello!");
    // Border
    tft.drawRect(0, 0, 240, 240, 0xFFFF);
}

The following minimal sketch follows Adafruit's official 240×240 ST7789 example. Replace the pin definitions and init() arguments with those from the working example for your module.

cpp
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>

#define TFT_CS 10
#define TFT_DC 8
#define TFT_RST 9

Adafruit_ST7789 tft(TFT_CS, TFT_DC, TFT_RST);

// Paste the complete Lopaka-generated block here.
void drawMain(void) {
    tft.fillScreen(0x0);
    // Greeting
    tft.setTextColor(0xFFFF);
    tft.setCursor(8, 12);
    tft.print("Hello!");
    // Border
    tft.drawRect(0, 0, 240, 240, 0xFFFF);
}

void setup(void) {
    tft.init(240, 240);
    tft.setRotation(0);
    drawMain();
}

void loop(void) {}

Color TFT drivers commonly draw directly to the display, so this example needs no separate present or flush call. Follow the hardware-specific example if your driver buffers output.

SSD1306 OLED monochrome example

For AdafruitGFX Mono, the wrapper includes the clear and refresh calls:

cpp
void drawMain(void) {
    display.clearDisplay();
    // Greeting
    display.setTextColor(1);
    display.setCursor(8, 12);
    display.print("Hello!");
    // Border
    display.drawRect(0, 0, 128, 64, 1);
    display.display();
}

This minimal sketch follows Adafruit's official 128×64 I2C SSD1306 example. Replace the reset pin and I2C address with the values for your module.

cpp
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3D

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Paste the complete Lopaka-generated block here.
void drawMain(void) {
    display.clearDisplay();
    // Greeting
    display.setTextColor(1);
    display.setCursor(8, 12);
    display.print("Hello!");
    // Border
    display.drawRect(0, 0, 128, 64, 1);
    display.display();
}

void setup(void) {
    if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
        for (;;) {}
    }
    drawMain();
}

void loop(void) {}

Step 7: Add runtime data and more screens

To make a supported property dynamic, enable its variable toggle in the Inspector and keep Declare variables enabled. Lopaka replaces the literal with a declaration based on the layer name. Update that value in your application before calling the drawing function again.

Each Lopaka screen exports separately, and its title becomes the wrapper function name. Copy each screen function into the sketch, merge duplicate includes and assets, then let your application decide which function to call:

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

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

Lopaka handles vs. you handle

Lopaka handlesYou handle
Pixel layout, layer order, and supported GFX callsExact display driver, constructor, pins, bus, and voltage
RGB565 colors for the color targetDriver-specific color order, offsets, and initialization
One-bit and RGB565 bitmap declarationsFlash/RAM budget and any filesystem or SD-card loading
GFX font selection and downloadable local headersInstalling libraries and keeping headers with the source
Optional variables and one-bit animation helpersRefresh timing, input, navigation, and application state
A named drawing function for each exported screenCalling that function and presenting buffered output correctly

Compile, upload, and iterate

  1. Compile before uploading. Resolve missing headers, undefined objects, and driver-specific method errors first.
  2. Upload and compare the physical display with the Lopaka canvas.
  3. Adjust the driver variant, address, rotation, canvas size, or layer positions as needed.
  4. When the design changes, replace the complete generated block instead of maintaining individual draw calls by hand.

Keep hardware and application code outside the generated section:

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

Troubleshooting

The display is blank

  • Restore and upload the driver's official example. If it is also blank, check power, voltage, ground, wiring, controller, address, reset, and bus pins.
  • Confirm that the driver's initialization method succeeds before calling the Lopaka function.
  • Confirm that the generated object name matches your instance: tft for color or display for mono.
  • For a buffered driver such as SSD1306, call its refresh method after drawing. Lopaka's mono template emits display.display(); another driver may require a different method.

The image is shifted, clipped, mirrored, or rotated

Match the Lopaka canvas to the logical dimensions after setRotation(). Use the exact driver variant for the panel: controller RAM can be larger than the visible area, and some modules need driver-specific offsets.

Colors are wrong

The color target emits RGB565 values. Confirm that the hardware-specific library expects standard Adafruit GFX 16-bit color values and that its initialization selects the correct panel variant. If red and blue are swapped, investigate the driver's color-order or panel configuration rather than changing every Lopaka color.

A custom font does not compile or align

  • Download its .h file from Code Settings → Fonts and place it beside the sketch.
  • Keep Include fonts enabled and confirm that the filename, included name, and GFX symbol match.
  • Remember that custom GFX fonts use a baseline and ignore the background argument to setTextColor().
  • Use fewer or smaller fonts if the program exceeds flash capacity.

An image does not compile or uses too much flash

Keep Declare images enabled if the generated draw call refers to an inline bitmap. Crop and resize the source to its final dimensions. Switch a simple icon from RGB to monochrome when one foreground color is enough.

Animation code conflicts with my sketch

Keep one setup() and one loop(). Retain the driver initialization from the official hardware example, then merge the generated animation header, update function, and draw functions. Lopaka animations use one-bit frames; they do not preserve RGB video frames.

FAQ

Does Lopaka support every Adafruit display?

Lopaka targets the shared Adafruit GFX drawing API, not a list of display modules. A display is a practical match when it has an Adafruit GFX-compatible driver for your Arduino or ESP32 environment and that driver supports the methods emitted for your design. Verify the exact module with its official driver example.

Does Lopaka install or configure Adafruit GFX?

No. Install the hardware-specific driver and its dependencies, create the display object, and initialize it in your sketch. Lopaka supplies drawing calls and assets.

Can the color target export full-color images?

Yes. RGB image layers generate RGB565 arrays and drawRGBBitmap() calls. Monochrome image layers and all animation frames remain one bit per pixel. Alpha and grayscale bitmap export are not supported by Lopaka's Adafruit templates.

Where should I learn more?