Examples

This section provides comprehensive examples demonstrating various features of ESP Emote GFX.

Initialization (core + display + optional touch)

Initialize the graphics core, add a display with flush callback, and optionally add touch. Widgets are created on the display (gfx_disp_t *disp).

#include "gfx.h"
#include "esp_check.h"
#include "esp_log.h"

static const char *TAG = "gfx_app";
static gfx_handle_t gfx_handle = NULL;
static gfx_disp_t *gfx_disp = NULL;

static void disp_flush_callback(gfx_disp_t *disp, int x1, int y1, int x2, int y2, const void *data)
{
    esp_lcd_panel_handle_t panel = (esp_lcd_panel_handle_t)gfx_disp_get_user_data(disp);
    esp_lcd_panel_draw_bitmap(panel, x1, y1, x2, y2, data);
}

static void touch_event_cb(gfx_touch_t *touch, const gfx_touch_event_t *event, void *user_data)
{
    ESP_LOGD(TAG, "touch type %d at (%d, %d)", event->type, event->x, event->y);
}

esp_err_t init_gfx(esp_lcd_panel_handle_t panel_handle, esp_lcd_touch_handle_t touch_handle)
{
    esp_err_t ret = ESP_OK;

    gfx_core_config_t gfx_cfg = {
        .fps = 30,
        .task = GFX_EMOTE_INIT_CONFIG(),
    };
    gfx_handle = gfx_emote_init(&gfx_cfg);
    ESP_GOTO_ON_FALSE(gfx_handle != NULL, ESP_FAIL, err_out, TAG, "Failed to init GFX");

    gfx_disp_config_t disp_cfg = {
        .h_res = 320,
        .v_res = 240,
        .flush_cb = disp_flush_callback,
        .update_cb = NULL,
        .user_data = (void *)panel_handle,
        .flags = { .swap = true },
        .buffers = { .buf1 = NULL, .buf2 = NULL, .buf_pixels = 320 * 16 },
    };
    gfx_disp = gfx_disp_add(gfx_handle, &disp_cfg);
    ESP_GOTO_ON_FALSE(gfx_disp != NULL, ESP_FAIL, err_gfx, TAG, "Failed to add display");

    if (touch_handle) {
        gfx_touch_config_t touch_cfg = {
            .handle = touch_handle,
            .event_cb = touch_event_cb,
            .disp = gfx_disp,
            .poll_ms = 50,
            .user_data = NULL,
        };
        if (gfx_touch_add(gfx_handle, &touch_cfg) == NULL) {
            ESP_LOGW(TAG, "Touch add failed");
        }
    }
    return ESP_OK;
err_gfx:
    gfx_emote_deinit(gfx_handle);
    gfx_handle = NULL;
err_out:
    return ret;
}

Basic Examples

Simple Label

Create and display a simple text label on a display (disp from gfx_disp_add):

#include "gfx.h"

void setup_label(gfx_disp_t *disp)
{
    gfx_obj_t *label = gfx_label_create(disp);
    gfx_label_set_text(label, "Hello, World!");
    gfx_obj_set_pos(label, 50, 50);
    gfx_label_set_color(label, GFX_COLOR_HEX(0xFF0000));
    gfx_disp_refresh_all(disp);
}

Image Display

Display an image:

#include "gfx.h"

void setup_image(gfx_disp_t *disp)
{
    gfx_obj_t *img = gfx_img_create(disp);
    extern const gfx_image_dsc_t my_image;
    gfx_img_set_src(img, (void *)&my_image);
    gfx_obj_align(img, GFX_ALIGN_CENTER, 0, 0);
}

Advanced Examples

Multiple Widgets

Create and manage multiple widgets on the same display:

#include "gfx.h"

void setup_widgets(gfx_disp_t *disp)
{
    gfx_obj_t *label = gfx_label_create(disp);
    gfx_label_set_text(label, "Status: OK");
    gfx_obj_set_pos(label, 10, 10);

    gfx_obj_t *img = gfx_img_create(disp);
    extern const gfx_image_dsc_t icon;
    gfx_img_set_src(img, (void *)&icon);
    gfx_obj_set_pos(img, 10, 50);

    gfx_obj_t *anim = gfx_anim_create(disp);
    gfx_anim_set_src(anim, anim_data, anim_size);
    gfx_obj_set_size(anim, 100, 100);
    gfx_obj_set_pos(anim, 150, 50);
    gfx_anim_set_segment(anim, 0, 10, 30, true);
    gfx_anim_start(anim);
}

Touch and object callback (e.g. drag)

Register a per-object touch callback so the object receives PRESS/MOVE/RELEASE (e.g. for dragging):

#include "gfx.h"

static int32_t drag_off_x, drag_off_y;

static void obj_touch_cb(gfx_obj_t *obj, const gfx_touch_event_t *event, void *user_data)
{
    gfx_coord_t ox, oy;
    gfx_obj_get_pos(obj, &ox, &oy);
    if (event->type == GFX_TOUCH_EVENT_PRESS) {
        drag_off_x = (int32_t)event->x - ox;
        drag_off_y = (int32_t)event->y - oy;
    }
    if (event->type == GFX_TOUCH_EVENT_MOVE) {
        gfx_obj_set_pos(obj, (int32_t)event->x - drag_off_x, (int32_t)event->y - drag_off_y);
    }
}

void make_draggable_label(gfx_disp_t *disp)
{
    gfx_obj_t *label = gfx_label_create(disp);
    gfx_label_set_text(label, "Drag me");
    gfx_obj_set_pos(label, 50, 50);
    gfx_obj_set_touch_cb(label, obj_touch_cb, NULL);
}

Text Scrolling

Create a scrolling text label (see widget API for gfx_label_set_long_mode, gfx_label_set_scroll_speed, etc.):

#include "gfx.h"

void setup_scroll_label(gfx_disp_t *disp)
{
    gfx_obj_t *label = gfx_label_create(disp);
    gfx_label_set_text(label, "This is a very long text that will scroll horizontally");
    gfx_obj_set_size(label, 200, 30);
    gfx_obj_set_pos(label, 10, 100);
    gfx_label_set_long_mode(label, GFX_LABEL_LONG_SCROLL);
    gfx_label_set_scroll_speed(label, 30);
    gfx_label_set_scroll_loop(label, true);
}

Timer-Based Updates

Use the graphics timer to update widgets periodically. Timers are created on the handle:

#include "gfx.h"

static gfx_obj_t *label = NULL;
static int counter = 0;

static void timer_callback(void *user_data)
{
    gfx_handle_t handle = (gfx_handle_t)user_data;
    gfx_emote_lock(handle);
    if (label) {
        gfx_label_set_text_fmt(label, "Counter: %d", counter++);
    }
    gfx_emote_unlock(handle);
}

void setup_timer_label(gfx_handle_t handle, gfx_disp_t *disp)
{
    label = gfx_label_create(disp);
    gfx_obj_set_pos(label, 50, 50);
    gfx_timer_create(handle, timer_callback, 1000, handle);
}

QR Code Generation

Generate and display a QR code:

#include "gfx.h"

void setup_qrcode(gfx_disp_t *disp)
{
    gfx_obj_t *qrcode = gfx_qrcode_create(disp);
    gfx_qrcode_set_data(qrcode, "https://www.espressif.com");
    gfx_qrcode_set_size(qrcode, 200);
    gfx_qrcode_set_ecc(qrcode, GFX_QRCODE_ECC_MEDIUM);
    gfx_obj_align(qrcode, GFX_ALIGN_CENTER, 0, 0);
}

Thread-Safe Operations

When modifying widgets from another task, always use the graphics lock (on the handle):

#include "gfx.h"

void update_widgets_from_task(gfx_handle_t handle)
{
    gfx_emote_lock(handle);
    gfx_label_set_text(label, "Updated from task");
    gfx_obj_set_pos(img, new_x, new_y);
    gfx_emote_unlock(handle);
}

Complete Application Example

Initialization (core + one display), then create a label and refresh:

#include "gfx.h"
#include "esp_log.h"

static const char *TAG = "gfx_app";
static gfx_handle_t gfx_handle = NULL;
static gfx_disp_t *gfx_disp = NULL;

static void disp_flush_callback(gfx_disp_t *disp, int x1, int y1, int x2, int y2, const void *data)
{
    esp_lcd_panel_handle_t panel = (esp_lcd_panel_handle_t)gfx_disp_get_user_data(disp);
    esp_lcd_panel_draw_bitmap(panel, x1, y1, x2, y2, data);
}

void app_main(void)
{
    gfx_core_config_t gfx_cfg = {
        .fps = 30,
        .task = GFX_EMOTE_INIT_CONFIG(),
    };
    gfx_handle = gfx_emote_init(&gfx_cfg);
    if (gfx_handle == NULL) {
        ESP_LOGE(TAG, "Failed to initialize GFX");
        return;
    }

    gfx_disp_config_t disp_cfg = {
        .h_res = 320,
        .v_res = 240,
        .flush_cb = disp_flush_callback,
        .update_cb = NULL,
        .user_data = panel_handle,   // your esp_lcd_panel_handle_t
        .flags = { .swap = true },
        .buffers = { .buf1 = NULL, .buf2 = NULL, .buf_pixels = 320 * 16 },
    };
    gfx_disp = gfx_disp_add(gfx_handle, &disp_cfg);
    if (gfx_disp == NULL) {
        ESP_LOGE(TAG, "Failed to add display");
        gfx_emote_deinit(gfx_handle);
        return;
    }

    gfx_obj_t *title = gfx_label_create(gfx_disp);
    gfx_label_set_text(title, "ESP Emote GFX");
    gfx_obj_align(title, GFX_ALIGN_TOP_MID, 0, 10);
    gfx_label_set_color(title, GFX_COLOR_HEX(0x0000FF));

    gfx_disp_refresh_all(gfx_disp);
    ESP_LOGI(TAG, "GFX application started");
}

For more examples, see the test applications in the test_apps/ directory.