Knowing how to display custom OLED icons with Adafruit GFX bitmap arrays is what separates a generic sensor readout from a polished, professional-looking maker project. Instead of plain text showing “Temp: 28°C”, you can have a thermometer icon, a humidity droplet, and a WiFi symbol — all rendered crisply on your 128×64 OLED. This tutorial walks you through every step: converting images to bitmap arrays, understanding the Adafruit GFX drawing model, and embedding icons into your Arduino sketches for SSD1306 and SH1106 OLED displays.
OLED Displays and Adafruit GFX Library
The most common OLED modules used in Arduino/ESP32 projects are the 0.96-inch 128×64 and the 0.91-inch 128×32 panels, both driven by the SSD1306 or SH1106 controller. These modules are monochrome — each pixel is either on (white/blue/yellow) or off (black). This binary nature actually makes bitmap graphics very memory-efficient.
The Adafruit GFX library provides a unified graphics API across all display types. Paired with a display-specific driver library (Adafruit_SSD1306 for SSD1306 OLEDs), it gives you functions for drawing pixels, lines, rectangles, circles, text, and — most usefully for this guide — bitmaps.
Install both libraries through the Arduino Library Manager:
- Adafruit SSD1306 (search: “Adafruit SSD1306”)
- Adafruit GFX Library (installs automatically as a dependency)
For SH1106-based displays, install ThingPulse OLED SSD1306 or U8g2 instead — they use the same GFX concepts with slightly different API calls.
Understanding Bitmap Array Format
A bitmap array in Adafruit GFX is a byte array stored in program memory (PROGMEM) where each bit represents one pixel: 1 = pixel on, 0 = pixel off. The bits are packed MSB-first (most significant bit first) into bytes, row by row, left to right, top to bottom.
For a 16×16 icon, you need 16 rows × 16 pixels = 256 bits = 32 bytes. For a 32×32 icon: 32×32 = 1024 bits = 128 bytes. The formula is: ceil(width / 8) × height bytes.
Here is a simple 16×16 heart icon as an example:
const unsigned char heart_icon[] PROGMEM = {
0b00000000, 0b00000000, // row 1 (empty)
0b01100110, 0b01100110, // row 2
0b11111111, 0b11111110, // row 3
0b11111111, 0b11111110, // row 4
0b11111111, 0b11111110, // row 5
0b01111111, 0b11111100, // row 6
0b00111111, 0b11111000, // row 7
0b00011111, 0b11110000, // row 8
0b00001111, 0b11100000, // row 9
0b00000111, 0b11000000, // row 10
0b00000011, 0b10000000, // row 11
0b00000001, 0b00000000, // row 12
0b00000000, 0b00000000, // rows 13-16
0b00000000, 0b00000000,
0b00000000, 0b00000000,
0b00000000, 0b00000000
};
The PROGMEM keyword is critical — it stores the array in flash memory instead of RAM. On an Arduino Uno with only 2KB RAM, a few icon arrays can easily overflow SRAM. Always use PROGMEM for bitmaps and read them with pgm_read_byte() (Adafruit GFX does this automatically in drawBitmap()).
Converting Images to Bitmap Arrays
Drawing bitmaps by hand in binary (like the heart above) is tedious for complex icons. These tools automate the conversion:
1. image2cpp (Web Tool)
Visit javl.github.io/image2cpp. Upload your PNG/BMP image, set the output format to “Arduino code”, choose “Horizontal” bit orientation, and click Generate Code. The output is ready-to-paste PROGMEM array code.
Tips for best results:
- Use images that are already the target size (16×16, 24×24, 32×32) — avoid resizing after conversion
- Use pure black and white images (1-bit PNG) — grayscale gets dithered to monochrome
- Enable “Invert image colors” if your display shows the icon inverted
- Set “Background color” to match your display’s background (usually black)
2. GIMP with Script
Open your icon in GIMP, scale to target size (Image → Scale Image), convert to 1-bit (Image → Mode → Indexed, 2 colours, no dithering), export as XBM format, then paste the XBM hex data into image2cpp for conversion to Adafruit format.
3. LCD Assistant (Windows)
A classic tool for embedded graphics — open your BMP, set byte orientation to horizontal, MSB first, and export. Works well for Windows users who prefer a desktop app.
DHT20 SIP Packaged Temperature and Humidity Sensor
I2C temperature and humidity sensor — perfect for OLED display projects showing real-time sensor readings alongside custom bitmap icons.
Using drawBitmap() in Your Sketch
The core function for displaying icons in Adafruit GFX is:
display.drawBitmap(x, y, bitmap_array, width, height, color);
Parameters:
x, y— top-left corner position in pixelsbitmap_array— pointer to your PROGMEM arraywidth, height— icon dimensions in pixelscolor—WHITE(1) for lit pixels,BLACK(0) for dark background (or vice versa)
Full working example:
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// 16x16 thermometer icon
const unsigned char thermo_icon[] PROGMEM = {
0x06,0x00, 0x09,0x00, 0x09,0x00, 0x09,0x00,
0x09,0x00, 0x0F,0x00, 0x0F,0x00, 0x0F,0x00,
0x1F,0x80, 0x3F,0xC0, 0x3F,0xC0, 0x1F,0x80,
0x1F,0x80, 0x3F,0xC0, 0x1F,0x80, 0x00,0x00
};
void setup() {
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
// Draw icon at position (5, 24)
display.drawBitmap(5, 24, thermo_icon, 16, 16, WHITE);
// Draw text beside icon
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(30, 22);
display.print("28.5C");
display.display();
}
Building a Reusable Icon Library
Instead of embedding icons in each sketch, create a separate header file icons.h that you include across projects. Organize icons by category:
// icons.h
#pragma once
#include <avr/pgmspace.h>
// Weather icons (16x16)
extern const unsigned char icon_sunny[] PROGMEM;
extern const unsigned char icon_cloudy[] PROGMEM;
extern const unsigned char icon_rainy[] PROGMEM;
// Sensor icons (16x16)
extern const unsigned char icon_thermometer[] PROGMEM;
extern const unsigned char icon_humidity[] PROGMEM;
extern const unsigned char icon_pressure[] PROGMEM;
// Status icons (16x16)
extern const unsigned char icon_wifi[] PROGMEM;
extern const unsigned char icon_wifi_off[] PROGMEM;
extern const unsigned char icon_battery[] PROGMEM;
Then create icons.cpp with the actual arrays, built from image2cpp conversions. This approach keeps your main sketch clean and makes icon reuse trivial — just include icons.h in any project.
A good resource for ready-made icons is the Material Design Icons set — download the 24px or 16px PNG variants, convert with image2cpp, and you have hundreds of professional icons ready for your OLED projects.
LM35 Temperature Sensors
Analog temperature sensor that pairs perfectly with Arduino OLED displays. Show real-time temperature alongside a custom thermometer bitmap icon.
Animating Icons with Frame Arrays
Since Adafruit GFX allows you to call drawBitmap() in a loop, you can create simple animations by cycling through multiple frames of an icon. This works great for loading spinners, blinking WiFi indicators, or animated battery charging icons.
// 4-frame WiFi scan animation (16x16 each)
const unsigned char wifi_anim[4][32] PROGMEM = {
{ /* frame 0 - just bottom bar */ },
{ /* frame 1 - bottom two bars */ },
{ /* frame 2 - three bars */ },
{ /* frame 3 - full signal */ }
};
int frame = 0;
void loop() {
display.clearDisplay();
display.drawBitmap(56, 24, wifi_anim[frame], 16, 16, WHITE);
display.display();
frame = (frame + 1) % 4;
delay(200);
}
For smoother animations without full display clears (which cause flicker), use fillRect() to erase only the icon area before drawing the next frame:
display.fillRect(56, 24, 16, 16, BLACK); // erase previous frame
display.drawBitmap(56, 24, wifi_anim[frame], 16, 16, WHITE);
display.display();
This technique keeps the rest of the screen static (temperature readings, labels) while animating just the icon — very useful for status indicators.
Real Project: Sensor Display with Icons
Here is a complete real-world scenario: an OLED display showing temperature, humidity, and air quality with bitmap icons beside each value. This is a popular project among Indian makers for room monitoring, lab environments, and industrial panels.
Hardware needed: Arduino Uno or ESP32, SSD1306 0.96″ OLED (I2C), DHT20 or DHT11 sensor.
Display layout for 128×64 OLED:
- Row 1 (y=0): Title bar — “Room Monitor” in small text, clock if RTC present
- Row 2 (y=16): Thermometer icon (16×16) + “28.5°C” in size-2 font
- Row 3 (y=38): Humidity droplet icon (16×16) + “65%” in size-2 font
For a 128×32 OLED, reduce font size to 1 and use 12×12 icons. The key is planning your layout on graph paper first — map out pixel coordinates before writing code.
The final result: a compact, icon-rich sensor display that looks professional and is immediately readable at a glance. Indian Instagrammers and YouTube makers consistently show that icon-based OLED displays get far more engagement than plain text readouts — the visual polish makes all the difference.
BMP280 Barometric Pressure and Altitude Sensor I2C/SPI Module
Add barometric pressure to your OLED icon display. BMP280 shares the I2C bus with the SSD1306, so no extra GPIO pins needed on Arduino.
DHT11 Digital Relative Humidity and Temperature Sensor Module
The most popular sensor for OLED display projects in India. Reads temperature and humidity with a single data pin — minimal wiring, maximum results.
Frequently Asked Questions
Q: What is the maximum icon size for a 128×64 OLED?
Technically up to 128×64 (full screen), but practically you will use 16×16 to 32×32 icons alongside text. A 64×64 icon takes 512 bytes of PROGMEM — manageable on most Arduino boards.
Q: My bitmap icon appears mirrored or inverted. How do I fix it?
Horizontal mirror: change image2cpp from “Horizontal” to “Horizontal reversed” byte orientation. Vertical mirror: flip the image before converting. Color inversion (icon is black on white instead of white on black): swap WHITE and BLACK in your drawBitmap() call, or invert the image in image2cpp.
Q: Can I use Adafruit GFX with U8g2-based OLED libraries?
U8g2 has its own bitmap functions (drawXBMP(), drawBitmap()) with a different byte format (LSB-first for XBM). image2cpp has a “XBM” output option for U8g2 compatibility. The two libraries are not directly interchangeable.
Q: I get a compilation error about PROGMEM not being defined.
Add #include <avr/pgmspace.h> at the top of your sketch. On ESP32, PROGMEM is defined in the Arduino ESP32 core but places data in flash — the syntax is identical.
Q: Are there any pre-made icon libraries for Arduino OLED displays?
Yes — search GitHub for “Arduino OLED icons” or “SSD1306 bitmap library”. The Adafruit_GFX_AS and community icon sets on GitHub have weather icons, arrows, and common symbols. The ESP32 community has several well-maintained icon sets for IoT projects.
Build Your OLED Display Project with Components from Zbotic
Zbotic carries everything you need for your OLED bitmap project — sensors, OLED modules, and microcontroller boards — with fast delivery across India. Start building today!
Add comment