ALI TABRANI ISMAELMAHATIR MANDING
Published © GPL3+

Lucky 3: Interactive HMI Game on RT-Thread

RT-Thread HMI reflex game featuring a polished, zero-code UI design built with SquareLine Studio and LVGL.

BeginnerProtip3 hours139
Lucky 3: Interactive HMI Game on RT-Thread

Things used in this project

Hardware components

RT-Thread Renesas RA6M3
×1

Software apps and online services

RT-Thread IoT OS
RT-Thread IoT OS
SquareLine Studio

Story

Read more

Schematics

FLOWCHART

Code

ui.h

C/C++
#ifndef _SQUARELINE_PROJECT_UI_H
#define _SQUARELINE_PROJECT_UI_H

#ifdef __cplusplus
extern "C" {
#endif

#include <lvgl.h>

#include "ui_helpers.h"
#include "components/ui_comp.h"
#include "components/ui_comp_hook.h"
#include "ui_events.h"


///////////////////// SCREENS ////////////////////

#include "screens/ui_Screen1.h"

///////////////////// VARIABLES ////////////////////

extern lv_anim_t * pop_Animation(lv_obj_t * TargetObject, int delay);

// EVENTS

extern lv_obj_t * ui____initial_actions0;

// IMAGES AND IMAGE SETS
LV_IMG_DECLARE(ui_img_untitled_design_png);    // assets/Untitled design.png
LV_IMG_DECLARE(ui_img_1570748651);    // assets/Untitled design (2).png
LV_IMG_DECLARE(ui_img_1570745578);    // assets/Untitled design (1).png
LV_IMG_DECLARE(ui_img_player_1_win_png);    // assets/PLAYER 1 WIN.png
LV_IMG_DECLARE(ui_img_player_2_win_png);    // assets/PLAYER 2 WIN.png
LV_IMG_DECLARE(ui_img_974432646);    // assets/haha 0-0 (1).png

// UI INIT
void ui_init(void);
void ui_destroy(void);

#ifdef __cplusplus
} /*extern "C"*/
#endif

#endif

ui.c

C/C++
#include "ui.h"
#include "ui_helpers.h"

///////////////////// VARIABLES ////////////////////
lv_anim_t * pop_Animation(lv_obj_t * TargetObject, int delay);

// EVENTS
lv_obj_t * ui____initial_actions0;

// IMAGES AND IMAGE SETS

///////////////////// TEST LVGL SETTINGS ////////////////////
#if LV_COLOR_DEPTH != 16
    #error "LV_COLOR_DEPTH should be 16bit to match SquareLine Studio's settings"
#endif
#if LV_COLOR_16_SWAP !=0
    #error "LV_COLOR_16_SWAP should be 0 to match SquareLine Studio's settings"
#endif

///////////////////// ANIMATIONS ////////////////////
lv_anim_t * pop_Animation(lv_obj_t * TargetObject, int delay)
{
    lv_anim_t * out_anim;
    ui_anim_user_data_t * PropertyAnimation_0_user_data = lv_mem_alloc(sizeof(ui_anim_user_data_t));
    PropertyAnimation_0_user_data->target = TargetObject;
    PropertyAnimation_0_user_data->val = -1;
    lv_anim_t PropertyAnimation_0;
    lv_anim_init(&PropertyAnimation_0);
    lv_anim_set_time(&PropertyAnimation_0, 200);
    lv_anim_set_user_data(&PropertyAnimation_0, PropertyAnimation_0_user_data);
    lv_anim_set_custom_exec_cb(&PropertyAnimation_0, _ui_anim_callback_set_opacity);
    lv_anim_set_values(&PropertyAnimation_0, 0, 255);
    lv_anim_set_path_cb(&PropertyAnimation_0, lv_anim_path_linear);
    lv_anim_set_delay(&PropertyAnimation_0, delay + 0);
    lv_anim_set_deleted_cb(&PropertyAnimation_0, _ui_anim_callback_free_user_data);
    lv_anim_set_playback_time(&PropertyAnimation_0, 0);
    lv_anim_set_playback_delay(&PropertyAnimation_0, 0);
    lv_anim_set_repeat_count(&PropertyAnimation_0, 0);
    lv_anim_set_repeat_delay(&PropertyAnimation_0, 0);
    lv_anim_set_early_apply(&PropertyAnimation_0, false);
    lv_anim_set_get_value_cb(&PropertyAnimation_0, &_ui_anim_callback_get_opacity);
    out_anim = lv_anim_start(&PropertyAnimation_0);
    ui_anim_user_data_t * PropertyAnimation_1_user_data = lv_mem_alloc(sizeof(ui_anim_user_data_t));
    PropertyAnimation_1_user_data->target = TargetObject;
    PropertyAnimation_1_user_data->val = -1;
    lv_anim_t PropertyAnimation_1;
    lv_anim_init(&PropertyAnimation_1);
    lv_anim_set_time(&PropertyAnimation_1, 250);
    lv_anim_set_user_data(&PropertyAnimation_1, PropertyAnimation_1_user_data);
    lv_anim_set_custom_exec_cb(&PropertyAnimation_1, _ui_anim_callback_set_image_zoom);
    lv_anim_set_values(&PropertyAnimation_1, 307, 256);
    lv_anim_set_path_cb(&PropertyAnimation_1, lv_anim_path_overshoot);
    lv_anim_set_delay(&PropertyAnimation_1, delay + 0);
    lv_anim_set_deleted_cb(&PropertyAnimation_1, _ui_anim_callback_free_user_data);
    lv_anim_set_playback_time(&PropertyAnimation_1, 0);
    lv_anim_set_playback_delay(&PropertyAnimation_1, 0);
    lv_anim_set_repeat_count(&PropertyAnimation_1, 0);
    lv_anim_set_repeat_delay(&PropertyAnimation_1, 0);
    lv_anim_set_early_apply(&PropertyAnimation_1, false);
    out_anim = lv_anim_start(&PropertyAnimation_1);

    return out_anim;
}

///////////////////// FUNCTIONS ////////////////////

///////////////////// SCREENS ////////////////////

void ui_init(void)
{
    LV_EVENT_GET_COMP_CHILD = lv_event_register_id();

    lv_disp_t * dispp = lv_disp_get_default();
    lv_theme_t * theme = lv_theme_default_init(dispp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED),
                                               false, LV_FONT_DEFAULT);
    lv_disp_set_theme(dispp, theme);
    ui_Screen1_screen_init();
    ui____initial_actions0 = lv_obj_create(NULL);
    lv_disp_load_scr(ui_Screen1);
}

void ui_destroy(void)
{
    ui_Screen1_screen_destroy();
}

ui.event.c

C/C++
#include "ui.h"
#include <stdlib.h> // For rand()
#include <stdio.h>  // For sprintf()

// --- Global Game Variables ---
int score_p1 = 0;
int score_p2 = 0;
int game_over_flag = 0;

// --- Helper: Hide everything ---
void hide_all_cards(void) {
    lv_obj_add_flag(ui_uiimgblue, LV_OBJ_FLAG_HIDDEN);
    lv_obj_add_flag(ui_uiimgred, LV_OBJ_FLAG_HIDDEN);
    lv_obj_add_flag(ui_uiimgfun, LV_OBJ_FLAG_HIDDEN);
    lv_obj_add_flag(ui_uiimgp1win, LV_OBJ_FLAG_HIDDEN);
    lv_obj_add_flag(ui_uiimgp2win, LV_OBJ_FLAG_HIDDEN);
}

// --- LOGIC: Reset Game to 0-0 ---
void perform_reset(void) {
    score_p1 = 0;
    score_p2 = 0;
    game_over_flag = 0;

    // Reset Text
    lv_label_set_text(ui_uilblscore1, "0");
    lv_label_set_text(ui_uilblscore2, "0");

    // Hide everything
    hide_all_cards();

    // Unlock Button
    lv_obj_clear_state(ui_uibtntoss, LV_STATE_DISABLED);
}

// --- TIMER 1: Normal Turn Callback ---
void auto_hide_callback(lv_timer_t * timer)
{
    lv_obj_add_flag(ui_uiimgblue, LV_OBJ_FLAG_HIDDEN);
    lv_obj_add_flag(ui_uiimgred, LV_OBJ_FLAG_HIDDEN);
    lv_obj_add_flag(ui_uiimgfun, LV_OBJ_FLAG_HIDDEN);
    lv_obj_clear_state(ui_uibtntoss, LV_STATE_DISABLED);
}

// --- TIMER 2: Win Reset Callback ---
void auto_reset_callback(lv_timer_t * timer)
{
    perform_reset();
}

// --- ANIMATION: The "Pop" Effect ---
void play_pop_animation(lv_obj_t * target_img)
{
    lv_img_set_zoom(target_img, 0); // Start tiny
    lv_anim_t a;
    lv_anim_init(&a);
    lv_anim_set_var(&a, target_img);
    lv_anim_set_values(&a, 0, 256); // Zoom to normal
    lv_anim_set_time(&a, 300);      // Speed
    lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_img_set_zoom);
    lv_anim_set_path_cb(&a, lv_anim_path_overshoot); // Bounce effect
    lv_anim_start(&a);
}

// =======================================================
//   MAIN GAME EVENT
// =======================================================

// Assign this to: uibtntoss (Event: Clicked)
void OnTossClicked(lv_event_t * e)
{
    if (game_over_flag == 1) return; // Stop if game is over

    // Disable button immediately
    lv_obj_add_state(ui_uibtntoss, LV_STATE_DISABLED);
    hide_all_cards();

    int outcome = rand() % 3;
    char buffer[10];

    // --- DETERMINE RESULT ---
    if (outcome == 0) {
        // BLUE
        lv_obj_clear_flag(ui_uiimgblue, LV_OBJ_FLAG_HIDDEN);
        play_pop_animation(ui_uiimgblue);
        score_p1++;
        sprintf(buffer, "%d", score_p1);
        lv_label_set_text(ui_uilblscore1, buffer);
    }
    else if (outcome == 1) {
        // RED
        lv_obj_clear_flag(ui_uiimgred, LV_OBJ_FLAG_HIDDEN);
        play_pop_animation(ui_uiimgred);
        score_p2++;
        sprintf(buffer, "%d", score_p2);
        lv_label_set_text(ui_uilblscore2, buffer);
    }
    else {
        // FUN / JOKER
        lv_obj_clear_flag(ui_uiimgfun, LV_OBJ_FLAG_HIDDEN);
        play_pop_animation(ui_uiimgfun);
    }

    // --- CHECK FOR WINNER ---
    if (score_p1 >= 3 || score_p2 >= 3) {
        game_over_flag = 1;

        // Show the correct Win Popup
        if (score_p1 >= 3) {
            lv_obj_clear_flag(ui_uiimgp1win, LV_OBJ_FLAG_HIDDEN);
            play_pop_animation(ui_uiimgp1win);
        } else {
            lv_obj_clear_flag(ui_uiimgp2win, LV_OBJ_FLAG_HIDDEN);
            play_pop_animation(ui_uiimgp2win);
        }

        // TIMING CHANGE: 5 Seconds (5000ms) for Win Screen
        lv_timer_t * t = lv_timer_create(auto_reset_callback, 5000, NULL);
        lv_timer_set_repeat_count(t, 1);
    }
    else {
        // --- TIMING LOGIC FOR NORMAL TURNS ---
        if (outcome == 2) {
            // Fun Image = 2.0 Seconds (2000ms)
            lv_timer_t * t = lv_timer_create(auto_hide_callback, 2000, NULL);
            lv_timer_set_repeat_count(t, 1);
        } else {
            // Blue or Red Image = 1.5 Seconds (1500ms)
            lv_timer_t * t = lv_timer_create(auto_hide_callback, 1500, NULL);
            lv_timer_set_repeat_count(t, 1);
        }
    }
}

// Optional: Logic for a manual restart button
void OnRestartGame(lv_event_t * e) {
    perform_reset();
}

ui_helpers.c

C/C++
// This file was generated by SquareLine Studio
// SquareLine Studio version: SquareLine Studio 1.5.4
// LVGL version: 8.3.11
// Project name: SquareLine_Project

#include "ui_helpers.h"

void _ui_bar_set_property(lv_obj_t * target, int id, int val)
{
    if(id == _UI_BAR_PROPERTY_VALUE_WITH_ANIM) lv_bar_set_value(target, val, LV_ANIM_ON);
    if(id == _UI_BAR_PROPERTY_VALUE) lv_bar_set_value(target, val, LV_ANIM_OFF);
}

void _ui_basic_set_property(lv_obj_t * target, int id, int val)
{
    if(id == _UI_BASIC_PROPERTY_POSITION_X) lv_obj_set_x(target, val);
    if(id == _UI_BASIC_PROPERTY_POSITION_Y) lv_obj_set_y(target, val);
    if(id == _UI_BASIC_PROPERTY_WIDTH) lv_obj_set_width(target, val);
    if(id == _UI_BASIC_PROPERTY_HEIGHT) lv_obj_set_height(target, val);
}


void _ui_dropdown_set_property(lv_obj_t * target, int id, int val)
{
    if(id == _UI_DROPDOWN_PROPERTY_SELECTED) lv_dropdown_set_selected(target, val);
}

void _ui_image_set_property(lv_obj_t * target, int id, uint8_t * val)
{
    if(id == _UI_IMAGE_PROPERTY_IMAGE) lv_img_set_src(target, val);
}

void _ui_label_set_property(lv_obj_t * target, int id, const char * val)
{
    if(id == _UI_LABEL_PROPERTY_TEXT) lv_label_set_text(target, val);
}


void _ui_roller_set_property(lv_obj_t * target, int id, int val)
{
    if(id == _UI_ROLLER_PROPERTY_SELECTED_WITH_ANIM) lv_roller_set_selected(target, val, LV_ANIM_ON);
    if(id == _UI_ROLLER_PROPERTY_SELECTED) lv_roller_set_selected(target, val, LV_ANIM_OFF);
}

void _ui_slider_set_property(lv_obj_t * target, int id, int val)
{
    if(id == _UI_SLIDER_PROPERTY_VALUE_WITH_ANIM) lv_slider_set_value(target, val, LV_ANIM_ON);
    if(id == _UI_SLIDER_PROPERTY_VALUE) lv_slider_set_value(target, val, LV_ANIM_OFF);
}


void _ui_screen_change(lv_obj_t ** target, lv_scr_load_anim_t fademode, int spd, int delay, void (*target_init)(void))
{
    if(*target == NULL)
        target_init();
    lv_scr_load_anim(*target, fademode, spd, delay, false);
}

void _ui_arc_increment(lv_obj_t * target, int val)
{
    int old = lv_arc_get_value(target);
    lv_arc_set_value(target, old + val);
    lv_event_send(target, LV_EVENT_VALUE_CHANGED, 0);
}

void _ui_bar_increment(lv_obj_t * target, int val, int anm)
{
    int old = lv_bar_get_value(target);
    lv_bar_set_value(target, old + val, anm);
}

void _ui_slider_increment(lv_obj_t * target, int val, int anm)
{
    int old = lv_slider_get_value(target);
    lv_slider_set_value(target, old + val, anm);
    lv_event_send(target, LV_EVENT_VALUE_CHANGED, 0);
}

void _ui_keyboard_set_target(lv_obj_t * keyboard, lv_obj_t * textarea)
{
    lv_keyboard_set_textarea(keyboard, textarea);
}

void _ui_flag_modify(lv_obj_t * target, int32_t flag, int value)
{
    if(value == _UI_MODIFY_FLAG_TOGGLE) {
        if(lv_obj_has_flag(target, flag)) lv_obj_clear_flag(target, flag);
        else lv_obj_add_flag(target, flag);
    }
    else if(value == _UI_MODIFY_FLAG_ADD) lv_obj_add_flag(target, flag);
    else lv_obj_clear_flag(target, flag);
}
void _ui_state_modify(lv_obj_t * target, int32_t state, int value)
{
    if(value == _UI_MODIFY_STATE_TOGGLE) {
        if(lv_obj_has_state(target, state)) lv_obj_clear_state(target, state);
        else lv_obj_add_state(target, state);
    }
    else if(value == _UI_MODIFY_STATE_ADD) lv_obj_add_state(target, state);
    else lv_obj_clear_state(target, state);
}


void _ui_textarea_move_cursor(lv_obj_t * target, int val)

{

    if(val == UI_MOVE_CURSOR_UP) lv_textarea_cursor_up(target);
    if(val == UI_MOVE_CURSOR_RIGHT) lv_textarea_cursor_right(target);
    if(val == UI_MOVE_CURSOR_DOWN) lv_textarea_cursor_down(target);
    if(val == UI_MOVE_CURSOR_LEFT) lv_textarea_cursor_left(target);
    lv_obj_add_state(target, LV_STATE_FOCUSED);
}

typedef void (*screen_destroy_cb_t)(void);

void scr_unloaded_delete_cb(lv_event_t * e)

{

    // Get the destroy callback from user_data

    screen_destroy_cb_t destroy_cb = lv_event_get_user_data(e);
    if(destroy_cb) {

        destroy_cb();  // call the specific screen destroy function

    }

}

void _ui_opacity_set(lv_obj_t * target, int val)
{
    lv_obj_set_style_opa(target, val, 0);
}

void _ui_anim_callback_free_user_data(lv_anim_t * a)
{
    lv_mem_free(a->user_data);
    a->user_data = NULL;
}

void _ui_anim_callback_set_x(lv_anim_t * a, int32_t v)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    lv_obj_set_x(usr->target, v);

}


void _ui_anim_callback_set_y(lv_anim_t * a, int32_t v)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    lv_obj_set_y(usr->target, v);

}


void _ui_anim_callback_set_width(lv_anim_t * a, int32_t v)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    lv_obj_set_width(usr->target, v);

}


void _ui_anim_callback_set_height(lv_anim_t * a, int32_t v)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    lv_obj_set_height(usr->target, v);

}


void _ui_anim_callback_set_opacity(lv_anim_t * a, int32_t v)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    lv_obj_set_style_opa(usr->target, v, 0);

}


void _ui_anim_callback_set_image_zoom(lv_anim_t * a, int32_t v)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    lv_img_set_zoom(usr->target, v);

}


void _ui_anim_callback_set_image_angle(lv_anim_t * a, int32_t v)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    lv_img_set_angle(usr->target, v);

}


void _ui_anim_callback_set_image_frame(lv_anim_t * a, int32_t v)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    usr->val = v;

    if(v < 0) v = 0;
    if(v >= usr->imgset_size) v = usr->imgset_size - 1;
    lv_img_set_src(usr->target, usr->imgset[v]);
}

int32_t _ui_anim_callback_get_x(lv_anim_t * a)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    return lv_obj_get_x_aligned(usr->target);

}


int32_t _ui_anim_callback_get_y(lv_anim_t * a)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    return lv_obj_get_y_aligned(usr->target);

}


int32_t _ui_anim_callback_get_width(lv_anim_t * a)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    return lv_obj_get_width(usr->target);

}


int32_t _ui_anim_callback_get_height(lv_anim_t * a)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    return lv_obj_get_height(usr->target);

}


int32_t _ui_anim_callback_get_opacity(lv_anim_t * a)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    return lv_obj_get_style_opa(usr->target, 0);

}

int32_t _ui_anim_callback_get_image_zoom(lv_anim_t * a)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    return lv_img_get_zoom(usr->target);

}

int32_t _ui_anim_callback_get_image_angle(lv_anim_t * a)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    return lv_img_get_angle(usr->target);

}

int32_t _ui_anim_callback_get_image_frame(lv_anim_t * a)

{

    ui_anim_user_data_t * usr = (ui_anim_user_data_t *)a->user_data;
    return usr->val;

}

void _ui_arc_set_text_value(lv_obj_t * trg, lv_obj_t * src, const char * prefix, const char * postfix)
{
    char buf[_UI_TEMPORARY_STRING_BUFFER_SIZE];

    lv_snprintf(buf, sizeof(buf), "%s%d%s", prefix, (int)lv_arc_get_value(src), postfix);

    lv_label_set_text(trg, buf);
}

void _ui_slider_set_text_value(lv_obj_t * trg, lv_obj_t * src, const char * prefix, const char * postfix)
{
    char buf[_UI_TEMPORARY_STRING_BUFFER_SIZE];

    lv_snprintf(buf, sizeof(buf), "%s%d%s", prefix, (int)lv_slider_get_value(src), postfix);

    lv_label_set_text(trg, buf);
}
void _ui_checked_set_text_value(lv_obj_t * trg, lv_obj_t * src, const char * txt_on, const char * txt_off)
{
    if(lv_obj_has_state(src, LV_STATE_CHECKED)) lv_label_set_text(trg, txt_on);
    else lv_label_set_text(trg, txt_off);
}


void _ui_spinbox_step(lv_obj_t * target, int val)

{

    if(val > 0) lv_spinbox_increment(target);

    else lv_spinbox_decrement(target);


    lv_event_send(target, LV_EVENT_VALUE_CHANGED, 0);
}

void _ui_switch_theme(int val)

{

#ifdef UI_THEME_ACTIVE
    ui_theme_set(val);
#endif
}

Credits

ALI TABRANI ISMAEL
1 project • 1 follower
MAHATIR MANDING
0 projects • 1 follower

Comments