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-30 17:31:25