Skip to content

Overview

The SSD1306 is an OLED display driver chip (OLED = organic light-emitting diode display, a self-emitting screen — every pixel glows on its own, no backlight needed; a pixel = the smallest "cell" of the screen); most common 0.96-inch OLED modules use it. A screen is essentially a dot matrix (a pattern made of little cells), and this tutorial drives a 128×64-resolution OLED screen over the I2C bus (a two-wire serial communication: SCL clock line sets the rhythm + SDA data line carries the content), displaying one line of date and time.

In plain words: an OLED screen is like a "mini TV", except each "pixel" (smallest glowing cell) lights up by itself — no backlight needed like old LCD screens. The I2C bus is like "two people on the phone": SCL is the rhythm of the speech, SDA is the content. The screen has a "door number" on the bus (I2C address 0x3C), and the program sends what to display to that door number — the screen draws the picture. This tutorial makes the screen display a hardcoded date and time.

This tutorial is based on the official Ai-Thinker SDK (Ai-Thinker-Open/Ai-Thinker-WB2, version release_bl_iot_sdk_1.6.40) example applications/iot-solution/demo_ssd1306; the code can be found directly in the local SDK.

🎯Page GoalDrive an SSD1306 OLED screen over the I2C bus to display date and time, and understand the dot-matrix screen driving approach.
🧰Prerequisites① An Ai-WB2 development board, a 0.96-inch SSD1306 OLED module, dupont wires ② Development environment set up per [SDK Installation](../sdk/sdk_intro).
🔗RelatedI2C fundamentals: [I2C Protocol](../basic/i2c); keep learning displays: [WS2812 RGB Light](./ws2812).

Hardware Wiring

Wire per the official example (I2C address 0x3C, the 7-bit address on the datasheet):

Ai-WB2 Pin SSD1306 Module
IO12 SCL (clock line)
IO3 SDA (data line)
3V3 VCC
GND GND

💡 Common 0.96-inch OLED modules have 4 pins (VCC / GND / SCL / SDA) — just hook each up with a dupont wire (jumper wire with pins at both ends). The I2C address = the device’s “door number” on the bus; the program finds this screen by 0x3C, so before wiring, confirm your module’s address is 0x3C (printed on the back silk screen or in the seller’s docs).

Enter the Example Project

This tutorial directly uses the demo_ssd1306 example project shipped with the official SDK; open a terminal and enter it:

cd ~/Ai-Thinker-WB2/applications/iot-solution/demo_ssd1306

Note: cd is the “change directory” command — entering the demo_ssd1306 project directory; all subsequent make build and make flash flash commands must run in this directory first.

📌 This is a multi-file project: there’s another demo_ssd1306/ subdirectory under the project root, and all source lives inside it (main.c + the screen driver ssd1306_drive.c/h). Keeping the driver in its own file pays off later: to reuse this screen, just copy the driver files away.

Project structure:

File Purpose
demo_ssd1306/main.c Main program source, the main file this tutorial looks at
demo_ssd1306/ssd1306_drive.c / .h SSD1306 screen driver (self-developed), wraps I2C init, display char/number and other interfaces
Makefile Build entry, usually no changes needed
Write the Code

Open demo_ssd1306/demo_ssd1306/main.c — the complete code for this step has been moved to the end of this page:

📜 Full Code — in the “Full Code” section below, collapsed by default — click to expand, identical to the official example (applications/iot-solution/demo_ssd1306/demo_ssd1306/main.c).

Code highlights:

Code Purpose
oled_i2c_driver_init(12, 3) Initializes I2C on IO12/IO3 and connects to the screen at address 0x3C; uninitialized, the screen never receives data
oled_time_output(2025, 4, 2, 12, 34, 56) Draws the date and time onto the screen’s dot matrix (year/month/day/hour/minute/second); wrong parameter order = garbled display
vTaskDelay(portTICK_RATE_MS * 1000) Refreshes the display every 1 second; without it the time draws once at power-on and never changes
Build the Project

Build in the project directory:

make -j8

Note: make is the “build” command, turning code into firmware (the program file) the board can run; -j8 builds with 8 parallel CPU cores, faster.

On success a firmware build_out/demo_ssd1306.bin is generated.

⚠️ If it reports riscv64-unknown-elf-gcc: command not found, the toolchain permissions aren’t configured — run cd toolchain/riscv/Linux && . chmod755.sh first, then rebuild.

Flash the Firmware

Keep the board connected via USB, confirm the serial device (usually /dev/ttyUSB0 on Linux), and flash:

make flash p=/dev/ttyUSB0 b=921600

Note: make flash is the “flash” command, writing the compiled firmware into the board’s chip. After p= comes the serial device (change it to your computer’s actual one — check with ls /dev/ttyUSB*), b= is the flash baud rate (transfer speed).

⏳ During flashing, press and hold the EN button on the board when prompted to enter download mode (some boards enter automatically); wait for the progress bar to complete — that means the flash succeeded.

Run and Verify

After flashing, the board automatically restarts and runs; watch the OLED screen:

The screen should show “Time”, then 2025/4/2 on the second line, then 12:34:56 on the third line (the time refreshes every second).

💡 The screen shows a hardcoded date and time from the example code: to show your own time, edit the 6 arguments of oled_time_output(2025, 4, 2, 12, 34, 56) in main.c (order: year, month, day, hour, minute, second), then rebuild and reflash. To light a single pixel, call oled_drive_set_pixels(x, y, color) from the driver (see ssd1306_drive.h).

Seeing the screen display the three lines “Time / year-month-day / hour:minute:second” normally means success; if the screen is completely dark (blank) or only shows part of the content, it hasn’t succeeded yet — check the FAQ at the end.


API Summary for This Tutorial

oled_i2c_driver_init(oled_scl, oled_sda)

Initializes the I2C controller and connects the SSD1306 screen (self-developed driver interface, source in ssd1306_drive.c). Internally initializes I2C in master mode with the 7-bit address 0x3C at 400KHz, and sends the screen a full set of init commands (display on, set contrast, etc.).

Parameters:

  • oled_scl: I2C clock pin number, this tutorial passes 12 (IO12)
  • oled_sda: I2C data pin number, this tutorial passes 3 (IO3)

Return: hosal_i2c_dev_t* I2C device handle; NULL on init failure

oled_time_output(yyyy, MM, dd, HH, mm, ss)

Draws the date and time onto the screen in three lines "Time / year-month-day / hour:minute:second" (self-developed driver interface, source in ssd1306_drive.c). Internally calls oled_output_char_num and similar to split each digit into 8×16 dot-matrix glyphs (dot matrix = a pattern made of cells), then calls oled_refresh_screen to refresh the screen.

Parameters:

  • yyyy: year, e.g. 2025
  • MM: month, 1~12
  • dd: day, 1~31
  • HH: hour, 0~23
  • mm: minute, 0~59
  • ss: second, 0~59

Return: 0 on success

vTaskDelay(ms)

Suspends the current task for the given milliseconds, yielding the CPU to other tasks (FreeRTOS system API).

Parameters:

  • ms: delay in milliseconds, values: any non-negative integer (this tutorial uses portTICK_RATE_MS * 1000 for 1 second)

Return: none


Full Code

Below is the complete demo_ssd1306/demo_ssd1306/main.c source, identical to the official example (applications/iot-solution/demo_ssd1306/demo_ssd1306/main.c):

📜 Click to expand the full demo_ssd1306/demo_ssd1306/main.c code
c
#include <stdio.h>

#include <FreeRTOS.h>
#include <task.h>

#include <hosal_i2c.h>
#include <bl_gpio.h>
#include <blog.h>

#include "ssd1306_drive.h"



int main(void)
{

    oled_i2c_driver_init(12, 3);

    for (;;) {
        
        oled_time_output(2025, 4, 2, 12, 34, 56);
        
        vTaskDelay(portTICK_RATE_MS * 1000);
    }

    return 0;
}

FAQ & Troubleshooting

⚠️ Screen completely dark (blank)
Cause: SCL/SDA swapped, the I2C address isn't 0x3C, or insufficient module power
Fix: check the wiring IO12=SCL, IO3=SDA (swapped = guaranteed blank); 0x3C is the 7-bit address notation — some modules print 0x78 (the 8-bit notation, the same address), confirm the module's silk screen

⚠️ Screen lights but shows garbage or flickers
Cause: poor dupont wire contact, wires too long, or unstable 3V3 supply
Fix: re-seat all dupont wires, shorten the wires; confirm the module's VCC is on 3V3 (5V may burn the module)

⚠️ Build reports No such file or directory (main.c not found)
Cause: this is a multi-file project — the source lives in the second-level directory demo_ssd1306/demo_ssd1306/
Fix: run the build command at the project root ~/Ai-Thinker-WB2/applications/iot-solution/demo_ssd1306; make finds the source automatically — don't cd into the second-level directory yourself

⚠️ Flashing reports cannot open the serial port
Cause: wrong serial device or insufficient permission
Fix: confirm the device with ls /dev/ttyUSB*; if permission denied run sudo usermod -aG dialout $USER and log back in

⚠️ Flashing keeps waiting, progress bar doesn't move
Cause: download mode wasn't entered, or the cable only charges and can't transfer data
Fix: press and hold EN during flashing to enter download mode as prompted; try a Type-C data-capable cable

Self-Check

The OLED screen displays the three lines "Time / 2025/4/2 / 12:34:56" normally with the time refreshing every second — the SSD1306 driver is verified.

Released under the MIT License. Build Time 2026-09-11 14:52:23