Repository navigation
U8g2 Displays
MicroCS can drive monochrome OLED, LCD and e-paper displays from C# through olikraus' u8g2 — SSD1306, SH1106, SSD1309, ST7920, ST7565, UC1701, PCD8544 (Nokia 5110), KS0108, SSD1607 e-paper and ~360 more panels on I²C, SPI or parallel pins. Both u8g2 APIs are wrapped completely:
-
U8g2— graphics with a frame buffer: text in 2000+ fonts, lines, boxes, circles, bitmaps, rotation, page mode for small RAM, buttons and menus. -
U8x8— text only, no frame buffer: 8×8 tiles go straight to the display (a few bytes of RAM).
var oled = new U8g2("ssd1306_i2c_128x64_noname"); // I2C bus 0, address 0x3C
oled.Begin();
oled.ClearBuffer();
oled.SetFont("helvB10_tr");
oled.DrawStr(0, 14, "Hello, MicroCS!");
oled.DrawCircle(64, 40, 16);
oled.SendBuffer();Step by step on a real board: ESP32-C3 + OLED guide (any ESP32 works the same way).
u8g2 support is optional and off by default: the u8g2 library is not bundled with MicroCS and is not in the release firmware. You turn it on in your own build, pick the displays and the built-in fonts there, and every other font can still be loaded from a file at run time.
- Try it on the PC
- Turn it on — CMake / pico-sdk / STM32 · ESP-IDF · Zephyr · Arduino · PlatformIO · make
- Choose displays and fonts
- Fonts from files
- C# API — creating · common · U8g2 · U8x8 · menus · log · constants
- Memory and flash · Errors · C API
make fetch-u8g2 # downloads u8g2 2.37.1 into build/third_party (once)
make mcs-u8g2 # -> build/mcs_u8g2 (the CLI with U8g2 / U8x8)
./build/mcs_u8g2 --oled examples/u8g2/hello.cs--oled attaches a simulated SSD1306 128×64 at I²C 0x3C to the simulated board and draws
every frame it receives on the terminal (--oled=sh1106, --oled=128x32, --oled=0x3D,
combined with commas). examples/u8g2/ has hello, graphics,
page mode, fonts, U8x8 text, menus and a scrolling log; make u8g2-test runs the tests.
In MicroCS Studio: Templates (Alt+T) → U8g2 displays has 20 ready-to-run scripts that
together use the whole API — hello, every bus (I²C, SPI, software, parallel, KS0108), all shapes,
text and font metrics, fonts from files, bitmaps and icons, rotation / contrast / sleep / clipping,
page mode, Print, buttons and text bars, the built-in menus, your own menu, the log terminal,
a dashboard, a clock, an animation, buffer screenshots, U8x8 text and status screens, two
displays. Snippets: u8g2, u8frame, u8page, u8x8; completion and hover know every
U8g2 / U8x8 member. On firmware without u8g2 the templates print a hint instead of failing.
CI runs each one against the simulated SSD1306
(node tests/studio/test_templates.js build/mcs_u8g2 --only=u8g2).
Every build looks for the u8g2 sources in the same places and stops with instructions when it cannot find them — nothing is downloaded unless you allow it:
- the path you give (
MICROCS_U8G2_DIR, also as an environment variable, orU8G2_DIR): a clone of github.com/olikraus/u8g2 (csrc/), the U8g2 Arduino library (src/clib/) or the folder withu8g2.hitself; -
u8g2/,third_party/u8g2,lib/u8g2,libs/u8g2,external/u8g2,components/u8g2in your project,../u8g2next to MicroCS,MicroCS/build/third_party/u8g2-*(python3 tools/u8g2.py fetch), an earlier download,~/Arduino/libraries/U8g2; - with
MICROCS_U8G2_DOWNLOAD=ONonly: u8g2 2.37.1 (the latest release) is downloaded once into the build folder.
python3 tools/u8g2.py where prints the folder that would be used.
| Build | Turn on | Sources / options |
|---|---|---|
| CMake (host, pico-sdk, STM32CubeMX, any toolchain) |
-DMICROCS_U8G2=ON, or #define MCS_ENABLE_U8G2 1 in the project's mcs_user_config.h (CMake sees it) |
-DMICROCS_U8G2_DIR=…, -DMICROCS_U8G2_DOWNLOAD=ON, -DMICROCS_U8G2_DISPLAYS="a;b", -DMICROCS_U8G2_FONTS="x;y", -DMICROCS_U8X8_FONTS=…
|
| ESP-IDF | menuconfig → MicroCS → U8g2 displays (CONFIG_MICROCS_U8G2=y) |
CONFIG_MICROCS_U8G2_DIR, CONFIG_MICROCS_U8G2_DOWNLOAD, CONFIG_MICROCS_U8G2_DISPLAYS="a b", …_FONTS, CONFIG_MICROCS_U8X8_FONTS
|
| Zephyr |
CONFIG_MICROCS_U8G2=y in prj.conf
|
the same CONFIG_MICROCS_U8G2_* options |
| Arduino IDE | install U8g2 (Library Manager), then python3 tools/make_arduino.py --u8g2
|
--u8g2-displays a,b --u8g2-fonts x,y --u8x8-fonts z (written into the library's src/mcs_user_config.h, where you can also edit them) |
| PlatformIO |
lib_deps = olikraus/U8g2 + build_flags = -DMCS_ENABLE_U8G2=1
|
the lists as -D flags (below) or in include/mcs_user_config.h
|
| make (host) | make mcs-u8g2 |
U8G2_DIR=…, U8G2_DISPLAYS="a b", U8G2_FONTS="x y", U8X8_FONTS=…; make fetch-u8g2 downloads |
| anything else | compile u8g2/csrc/*.c with -ffunction-sections -fdata-sections -DU8G2_USE_DYNAMIC_ALLOC, add csrc to the include path, -DMCS_ENABLE_U8G2=1, link with --gc-sections
|
the MCS_U8G2_* options in mcs_user_config.h
|
Examples:
# Raspberry Pi Pico, u8g2 next to the project or downloaded once
cmake -S ports/rp2/example -B build/pico -DPICO_SDK_PATH=$HOME/pico-sdk -DMICROCS_FS=tinyfs \
-DMICROCS_U8G2=ON -DMICROCS_U8G2_DOWNLOAD=ON \
"-DMICROCS_U8G2_DISPLAYS=ssd1306_i2c_128x64_noname;sh1106_i2c_128x64_noname" \
"-DMICROCS_U8G2_FONTS=helvB10_tr;6x10_tf;logisoso24_tn"; platformio.ini
lib_deps = olikraus/U8g2
build_flags = -DMCS_ENABLE_U8G2=1
'-DMCS_U8G2_DISPLAYS=U8G2_DISPLAY(ssd1306_i2c_128x64_noname)'
'-DMCS_U8G2_FONTS=U8G2_FONT(u8g2_font_helvB10_tr) U8G2_FONT(u8g2_font_6x10_tf)'Only the listed displays and fonts end up in the firmware: u8g2 is compiled with
-ffunction-sections -fdata-sections and the firmware is linked with --gc-sections
(pico-sdk, ESP-IDF, Zephyr, Arduino, PlatformIO, STM32CubeMX and the host build do this
already). The u8g2 sources get -w; MicroCS' own code stays warning-clean.
The names are u8g2's: a display is the setup function u8g2_Setup_<name>_f without
prefix and suffix, a font is u8g2_font_<name>. The helper lists them, with sizes:
python3 tools/u8g2.py displays ssd1306 # name, bus, size, frame buffer RAM
python3 tools/u8g2.py fonts helv # fonts with their size in bytes
python3 tools/u8g2.py fonts --u8x8 chroma
python3 tools/u8g2.py config --displays ssd1306_i2c_128x64_noname,st7920_s_128x64 \
--fonts helvB10_tr,6x10_tf --u8x8-fonts chroma48medium8_rconfig checks the names and prints the lines for mcs_user_config.h, CMake, make and
PlatformIO. In mcs_user_config.h (template: config/mcs_user_config.h):
#define MCS_ENABLE_U8G2 1
#define MCS_U8G2_DISPLAYS U8G2_DISPLAY(ssd1306_i2c_128x64_noname) U8G2_DISPLAY(st7920_s_128x64)
#define MCS_U8G2_FONTS U8G2_FONT(u8g2_font_helvB10_tr) U8G2_FONT(u8g2_font_6x10_tf) /* first = default */
#define MCS_U8X8_FONTS U8X8_FONT(u8x8_font_chroma48medium8_r)| Option | Default | |
|---|---|---|
MCS_ENABLE_U8G2 |
0 | the u8g2 driver (needs MCS_ENABLE_DRIVERS) |
MCS_U8G2_DISPLAYS |
SSD1306 128×64 + 128×32 and SH1106 128×64 on I²C, SSD1306 128×64 on SPI | displays compiled in (each costs 1–3 KB of flash) |
MCS_U8G2_FONTS |
6x10_tf (default font), 5x7_tr, helvB10_tr, ncenB14_tr, logisoso24_tn (digits), open_iconic_embedded_1x_t (icons) — ≈ 7 KB |
U8g2 fonts in flash |
MCS_U8X8_FONTS |
chroma48medium8_r, 8x13_1x2_r
|
U8x8 fonts in flash |
MCS_U8G2_FONT_DIR |
"/fonts" |
where SetFont("name") looks for files |
MCS_U8G2_MAX_FONTS |
4 | font files kept loaded per display (VM heap) |
MCS_U8G2_MAX_FONT_SIZE |
65536 | largest font file accepted |
MCS_U8G2_EXTRA |
8 | slots for mcs_u8g2_add_font() / mcs_u8g2_add_display()
|
MCS_U8G2_I2C_BUF |
34 | I²C transfer buffer per display |
All 2043 U8g2 and 131 U8x8 fonts stay usable without building them in. Extract the ones you want and put them on the board's filesystem:
python3 tools/u8g2.py extract profont12_tr unifont_t_symbols -o myfs/fonts
python3 tools/u8g2.py extract --u8x8 pxplusibmcga_f -o myfs/fonts
python3 tools/u8g2.py extract --all -o allfonts # all of them, ~15 MB
python3 tools/u8g2.py extract --from my_bdfconv_font.c -o myfs/fonts # your own (bdfconv output)This writes u8g2_font_<name>.bin / u8x8_font_<name>.bin — the font bytes exactly as u8g2
stores them. Copy them to /fonts (Studio, tools/mcs_remote.py, an SD card, or
--fs myfs on the PC). Then SetFont("profont12_tr") uses the built-in font if there is one,
else loads /fonts/u8g2_font_profont12_tr.bin into the VM heap (checked, kept for the next
call). Other ways: SetFont("/sd/big.bin") (a path) and SetFont(bytes) (a byte[]).
The classes exist when the firmware has the u8g2 driver: Hal.Has("U8g2"),
Drivers.Has("u8g2"). Names accept any spelling u8g2 uses — "ssd1306_i2c_128x64_noname",
"ssd1306_128x64_noname" (the bus picks the I²C or SPI variant), "…_1" / "…_2" / "…_f"
(page buffer size), "u8g2_Setup_ssd1306_i2c_128x64_noname_f" and Arduino class names
like "U8G2_SSD1306_128X64_NONAME_F_HW_I2C" / "U8X8_SH1106_128X64_NONAME_HW_I2C".
U8g2.Displays / U8g2.Fonts / U8x8.Fonts list what is compiled in.
| Pins | |
|---|---|
new U8g2(display) / U8g2.I2C(display[, bus[, address[, reset]]])
|
hardware I²C (bus 0, u8g2's default address 0x3C; give 0x3D as 7-bit) |
U8g2.SoftI2C(display, scl, sda[, reset]) |
bit-banged I²C on any two pins |
U8g2.SPI(display, bus, cs, dc[, reset]) |
hardware SPI (mode and clock from u8g2's display data) |
U8g2.SoftSPI(display, clock, data, cs, dc[, reset]) |
bit-banged 4-wire SPI |
U8g2.Soft3WireSPI(display, clock, data, cs[, reset]) |
3-wire SPI (9-bit, no DC pin) |
U8g2.Parallel8080(display, int[8] data, wr, cs, dc[, reset]) |
8080 parallel bus |
U8g2.Parallel6800(display, int[8] data, e, cs, dc[, reset]) |
6800 parallel bus |
U8g2.KS0108(display, int[8] data, e, dc, cs0, cs1, cs2[, reset]) |
KS0108 128×64 LCDs |
U8x8 has the same constructor and factories. Pins are numbers or board names
("GP4", "PA5"); null = not connected. The display is not touched before Begin().
| Member | |
|---|---|
Begin([select, next, prev[, up, down, home]]) |
init + clear + wake the display; the optional pins are menu buttons to GND |
InitDisplay(), ClearDisplay(), Clear(), RefreshDisplay()
|
u8g2's init / clear / refresh (e-paper) |
SetPowerSave(bool), Sleep(), Wake(), SetContrast(0..255), SetFlipMode(bool)
|
|
BusClock, SetBusClock(hz), I2CAddress, SetI2CAddress(addr)
|
used by the next Begin()
|
Display, Bus, Cols, Rows
|
setup name, bus kind, size in 8×8 tiles |
SetFont(name | path | byte[]), Font
|
see fonts |
SetCursor(x, y), Home(), CursorX, CursorY, Print(value), Println([value])
|
Arduino-style printing (U8g2: pixels, baseline; U8x8: tiles); \n starts a new line |
UserInterfaceSelectionList(title, start, "a\nb\nc") |
menu; returns the 1-based choice, 0 = home/abort |
UserInterfaceMessage(t1, t2, t3, " Ok \n Cancel ") |
message box; returns the button (1-based) |
UserInterfaceInputValue(title, pre, value, lo, hi, digits, post) |
number input; returns the value (-1 = aborted) |
GetMenuEvent(), SetMenuInput(Func<int>)
|
menus |
LogBegin(cols, rows[, autoRedraw = true]), Log(text), LogLine([text]), DrawLog(x, y), LogRedrawOnNewline, LogLineHeightOffset
|
u8log terminal |
Dispose() |
frees the buffers; the display keeps its picture |
| Group | Members |
|---|---|
| frame buffer |
ClearBuffer(), SendBuffer(), UpdateDisplay(), UpdateDisplayArea(tx, ty, tw, th), SetAutoPageClear(bool), BufferSize, FullBuffer, BufferTileWidth, BufferTileHeight, BufferCurrTileRow
|
| page loop |
FirstPage() / NextPage(), or Draw(() => …) / Draw(g => …) which runs the loop for you |
| buffer access |
GetBuffer() (byte[]), SetBuffer(byte[]), GetPixel(x, y), Dump() (text art), WriteBufferPBM(), WriteBufferXBM()
|
| shapes |
DrawPixel, DrawHLine, DrawVLine, DrawHVLine(x, y, len, dir), DrawLine, DrawBox, DrawFrame, DrawRBox, DrawRFrame, DrawCircle, DrawDisc, DrawEllipse, DrawFilledEllipse (optional quadrant: U8g2.DrawUpperRight…DrawAll), DrawArc(x, y, r, start, end) (0..255 = full turn), DrawTriangle, DrawPolygon(int[] xy)
|
| bitmaps |
DrawXBM(x, y, w, h, byte[]), DrawXBMP(…), DrawBitmap(x, y, bytesPerRow, h, byte[]), SetBitmapMode(transparent)
|
| text |
DrawStr, DrawUTF8, DrawGlyph(x, y, code), DrawStrX2, DrawUTF8X2, DrawGlyphX2, DrawUTF8Line(x, y, w, text, border, isInverted), DrawUTF8Lines(x, y, w, lineHeight, text), DrawButtonUTF8(x, y, flags, width, padH, padV, text), DrawButtonFrame(…)
|
| font metrics |
GetStrWidth, GetUTF8Width, GetXOffsetUTF8, GetXOffsetGlyph, GetGlyphWidth, IsGlyph, Ascent, Descent, MaxCharHeight, MaxCharWidth
|
| font settings |
SetFontMode(transparent), SetFontDirection(0..3), SetFontPosBaseline/Bottom/Top/Center(), SetFontRefHeightAll/ExtendedText/Text()
|
| drawing state |
SetDrawColor(0/1/2), DrawColor, SetClipWindow(x0, y0, x1, y1), SetMaxClipWindow(), IsIntersection(x0, y0, x1, y1), SetDisplayRotation(U8g2.R0..R3 / Mirror / MirrorVertical), Width, Height
|
Full buffer or page mode. Names ending in _f (the default) keep the whole picture in
RAM (128×64: 1 KB): draw, then SendBuffer(). _1 keeps one 8-pixel page (128 bytes) and
_2 two: draw inside Draw(...) (or FirstPage/NextPage), which runs your code once per
page. Print, DrawLog and the menus work in both modes.
ClearLine(row), FillDisplay(), DrawString(col, row, text), DrawUTF8, DrawGlyph(col, row, code),
Draw1x2String / Draw1x2UTF8 / Draw1x2Glyph, Draw2x2String / Draw2x2UTF8 / Draw2x2Glyph,
DrawTile(col, row, count, byte[8 * count]), SetInverseFont(bool), GetUTF8Len(text),
FontWidth, FontHeight (in tiles), plus the common members. Coordinates are tiles (8×8 pixels).
u8g2's three user-interface functions block until the user decides. The keys come from:
-
pins:
Begin(select, next, prev[, up, down, home])— buttons to GND, read with pull-ups by u8g2 itself; -
your code:
SetMenuInput(() => …)returnsU8g2.EventNext,EventPrev,EventSelect,EventHome,EventUp,EventDownorEventNone— from a rotary encoder, a touch pad, a remote, a test script.SetMenuInput(null)goes back to the pins.
GetMenuEvent() polls once (for your own menus). While a menu waits, timers, interrupts,
the time limit and Ctrl-C keep working (an abort leaves the menu with 0).
oled.SetFont("5x7_tr");
oled.LogBegin(25, 8); // 25 columns, 8 lines of text (200 bytes)
oled.Log($"T = {t} C\n"); // scrolls; redraws the screen on each newlineautoRedraw: false only collects the text; draw it yourself with DrawLog(x, y) (U8g2:
pixel position of the first baseline, U8x8: tile).
R0 R1 R2 R3 Mirror MirrorVertical (rotation) · ColorClear ColorSet ColorXor
· DrawUpperRight DrawUpperLeft DrawLowerLeft DrawLowerRight DrawAll · BtnBW0…BtnBW3
BtnShadow0…BtnShadow2 BtnInv BtnHCenter BtnXFrame · EventNone EventSelect
EventNext EventPrev EventHome EventUp EventDown (also on U8x8).
| frame buffer (VM heap) |
_f: width × height / 8 (128×64: 1024 bytes), _2: 2 × width, _1: width bytes; U8x8: none |
| per display | ≈ 200 bytes + the I²C buffer (MCS_U8G2_I2C_BUF) |
| font files | their size, while loaded (MCS_U8G2_MAX_FONTS per display) |
| flash | the driver ≈ 20 KB (Cortex-M, -Os) + the u8g2 code you use + displays (1–3 KB each) + built-in fonts (the default set ≈ 7 KB) — a Raspberry Pi Pico firmware grows by ≈ 95 KB with the default displays and fonts |
| Exception | When |
|---|---|
ArgumentException |
unknown display (the message lists the compiled-in ones), unknown font / no font file, invalid font data, wrong pin |
IOException |
no display answers at I2C address 0x3C on bus 0 — wiring, address or power; other bus errors |
NotSupportedException |
the board has no I²C / SPI bus with that number |
ObjectDisposedException |
after Dispose()
|
OutOfMemoryException |
the frame buffer does not fit the VM heap — use _1 / _2 or U8x8 |
Build errors are explicit too: MCS_ENABLE_U8G2=1 without the u8g2 headers stops with
needs the u8g2 library … python3 tools/u8g2.py fetch, CMake stops with the variables to
set, and a misspelled MICROCS_U8G2_DISPLAYS / _FONTS name is reported at configure time.
#include "mcs_driver.h"
/* a font of your own (bdfconv output) or a display the build does not list */
mcs_u8g2_add_font("my_font_tr", my_font_tr); /* SetFont("my_font_tr") */
mcs_u8x8_add_font("my_tiles", my_tiles);
MCS_U8G2_ADD_DISPLAY(st7920_s_128x64); /* needs u8g2.h in that file */Call them before the VM starts. The driver uses the port's HAL (i2c_*, spi_*, gpio_*,
delay); mcs_hal_spi_config() sets the SPI mode and clock per display. Driver sources:
modules/drivers/mcs_drv_u8g2.c; simulator:
modules/hal/mcs_hal_sim.c (SSD1306 / SH1106 emulation).
u8g2 is © olikraus, BSD 2-clause licensed; its fonts have their own (free) licences, listed in the u8g2 repository.
Generated from the MicroCS repository - edit the files there, not in the wiki.
Start here
Step-by-step guides
- Guides
- Guide PC Simulator
- Guide ESP32
- Guide ESP32 C3 OLED
- Guide Raspberry Pi Pico
- Guide STM32
- Guide Arduino
- Guide Zephyr
- Guide Peripheral Settings
- Guide DMA
Hardware
- Hardware API
- Power Management
- Bluetooth LE
- Peripheral Configuration
- DMA
- Device Drivers
- Wi Fi and Networking
- U8g2 Displays
- Filesystem
- Threads
- Scheduler
Ports
- Porting and Build Systems
- Port ESP32
- Port RP2040 RP2350
- Port STM32
- Port STM32 Firmware
- Port Zephyr
- Port Arduino
C# and the runtime
Embedding and tuning
Internals
Project