Skip to content

Instantly share code, notes, and snippets.

@maxgerhardt
Created August 9, 2026 18:29
Show Gist options
  • Select an option

  • Save maxgerhardt/dbf3864310a1b0cb0d22f8afda16c9c3 to your computer and use it in GitHub Desktop.

Select an option

Save maxgerhardt/dbf3864310a1b0cb0d22f8afda16c9c3 to your computer and use it in GitHub Desktop.
u8g2 + epaper reference ESP-IDF
#include <stdint.h>
#include <stdio.h>
#include "u8g2.h"
#include "epd_gdey042t81.h"
#define SCREEN_WIDTH 400
#define SCREEN_HEIGHT 300
#define BYTES_PER_ROW (SCREEN_WIDTH / 8)
#define FRAMEBUFFER_SIZE (BYTES_PER_ROW * SCREEN_HEIGHT)
#define TILE_HEIGHT ((SCREEN_HEIGHT + 7) / 8)
static u8g2_t u8g2;
/*
* This is BOTH:
*
* - u8g2's drawing framebuffer
* - the framebuffer passed to epd_display()
*
* No second framebuffer and no conversion.
*/
static uint8_t framebuffer[FRAMEBUFFER_SIZE];
/*
* u8g2 needs display geometry.
*
* tile_width = 400 / 8 = 50 bytes per row
* tile_height = ceil(300 / 8) = 38
*
* pixel_width = 400
* pixel_height = 300
*/
static const u8x8_display_info_t display_info = {
.chip_enable_level = 0,
.chip_disable_level = 1,
.post_chip_enable_wait_ns = 0,
.pre_chip_disable_wait_ns = 0,
.reset_pulse_width_ms = 0,
.post_reset_wait_ms = 0,
.sda_setup_time_ns = 0,
.sck_pulse_width_ns = 0,
.sck_clock_hz = 0,
.spi_mode = 0,
.i2c_bus_clock_100kHz = 0,
.data_setup_time_ns = 0,
.write_pulse_width_ns = 0,
.tile_width = SCREEN_WIDTH / 8,
.tile_height = TILE_HEIGHT,
.default_x_offset = 0,
.flipmode_x_offset = 0,
.pixel_width = SCREEN_WIDTH,
.pixel_height = SCREEN_HEIGHT,
};
/*
* u8g2 doesn't send anything to a physical display.
*
* We only use this callback to provide the display geometry.
*/
static uint8_t display_callback(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
(void)arg_int;
(void)arg_ptr;
if (msg == U8X8_MSG_DISPLAY_SETUP_MEMORY)
u8x8->display_info = &display_info;
return 1;
}
/*
* Initialize u8g2 as a framebuffer-only renderer.
*/
static void graphics_init(void)
{
u8g2_SetupDisplay(
&u8g2,
display_callback,
u8x8_dummy_cb,
u8x8_dummy_cb,
u8x8_dummy_cb
);
/*
* IMPORTANT:
*
* horizontal_right_lsb gives us:
*
* framebuffer[y * 50 + x / 8]
*
* with:
*
* x=0 -> bit 7
* x=1 -> bit 6
* ...
* x=7 -> bit 0
*
* This is exactly the format expected by epd_gdey042t81.c.
*/
u8g2_SetupBuffer(
&u8g2,
framebuffer,
TILE_HEIGHT,
u8g2_ll_hvline_horizontal_right_lsb,
U8G2_R0
);
}
/*
* Draw our screen.
*/
static void render_screen(void)
{
u8g2_ClearBuffer(&u8g2);
u8g2_SetDrawColor(&u8g2, 1);
u8g2_SetFont(&u8g2, u8g2_font_helvB24_tf);
u8g2_DrawStr(&u8g2, 20, 45, "Hello e-paper!");
u8g2_SetFont(&u8g2, u8g2_font_helvB14_tf);
u8g2_DrawStr(&u8g2, 20, 75, "u8g2 + GDEY042T81");
u8g2_DrawFrame(
&u8g2,
10,
100,
SCREEN_WIDTH - 20,
100
);
u8g2_DrawLine(
&u8g2,
20,
220,
SCREEN_WIDTH - 20,
220
);
u8g2_DrawDisc(
&u8g2,
SCREEN_WIDTH / 2,
260,
25,
U8G2_DRAW_ALL
);
}
/*
* Full display update.
*
* framebuffer is already in exactly the format expected by
* epd_display().
*/
static void display_full(void)
{
epd_display(framebuffer);
}
/*
* Fast full display update.
*/
static void display_full_fast(void)
{
epd_display_fast(framebuffer);
}
/*
* Establish the framebuffer as the partial-refresh base image.
*/
static void display_set_basemap(void)
{
epd_set_basemap(framebuffer);
}
void app_main(void)
{
printf("Starting graphics renderer\n");
printf("Screen: %d x %d\n", SCREEN_WIDTH, SCREEN_HEIGHT);
printf("Bytes/row: %d\n", BYTES_PER_ROW);
printf("Framebuffer: %d bytes\n", FRAMEBUFFER_SIZE);
/*
* Initialize the GDEY042T81.
*
* This also initializes the SPI interface.
*/
epd_init_fast();
/*
* Initialize u8g2.
*
* No u8g2 display hardware is involved.
*/
graphics_init();
/*
* Draw into framebuffer[].
*/
render_screen();
/*
* framebuffer[] is directly passed to the e-paper driver.
*/
display_full_fast();
/*
* Or:
*
* display_full();
*
* Or establish this image as the partial-refresh base:
*
* display_set_basemap();
*/
/*
* The pointer returned by u8g2 is the same buffer.
*/
uint8_t *fb = u8g2_GetBufferPtr(&u8g2);
printf("u8g2 framebuffer: %p\n", (void *)fb);
printf("our framebuffer: %p\n", (void *)framebuffer);
while (1)
vTaskDelay(pdMS_TO_TICKS(1000));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment