Where & How to Initiate a Timer for an Arc

Description

I am trying to create a simple countdown animated timer using a Arc.

What MCU/Processor/Board and compiler are you using?

M5Stack Core2 and Arduino IDE 2

What LVGL version are you using?

Latest version 8

What do you want to achieve?

For now, just trying to set a variable with a defined time in seconds and attach it to an Arc that disappears as the timer counts down.

I can manage to get LVGL working fine with the M5Core2 but not the timer. I am using the files that are produced by Squareline Studio as a guide - ui, events, helpers etc - but I am struggling with how to implement the timer animation.

Any guidance greatly appreciated.

Hi @Damien_Norris ,

Welcome to the LVGL forum…

I am not sure of your exact requirements here but this is a small code snippet which runs in the LVGL simulator which hopefully demonstrates the way the widget works. If you have further questions or want to expand on your requirement then please ask…

#define COUNT		10

static lv_obj_t		*arc_1;
static lv_obj_t		*klabel;
static uint32_t		timer_value = 0;

static void update_knob_label( lv_obj_t *label ) {

	/*Rotate the label to the current position of the arc*/
	lv_label_set_text_fmt(label, "%d", timer_value );
	lv_arc_rotate_obj_to_angle(arc_1, label, 35);
}

static void update_arc_cb( lv_timer_t *timer ) {

	if( timer_value ) lv_arc_set_value( arc_1, --timer_value );
	update_knob_label( klabel );
}

static void btn_cb( lv_event_t *e ) {

	timer_value = COUNT;
	lv_arc_set_value(arc_1, COUNT);
	update_knob_label( klabel );
}

void test( lv_obj_t *parent ) {

	klabel = lv_label_create( parent );
	lv_obj_set_style_text_color( klabel, lv_palette_main(LV_PALETTE_BLUE), LV_PART_MAIN);
	arc_1 = lv_arc_create(parent);
	timer_value = COUNT;

	lv_arc_set_mode(arc_1, LV_ARC_MODE_NORMAL);
	lv_arc_set_range(arc_1, 0, COUNT);
	lv_arc_set_angles(arc_1, 45, 315);
	lv_obj_set_style_arc_rounded(arc_1, 0, LV_PART_INDICATOR|LV_STATE_DEFAULT);
	lv_obj_set_style_arc_rounded(arc_1, 0, LV_STATE_DEFAULT);
	lv_obj_set_pos(arc_1,  120, 120);
	lv_obj_set_size(arc_1, 100, 100);
	lv_arc_set_value( arc_1, COUNT );
	update_knob_label( klabel );

	lv_obj_t *btn = lv_btn_create( parent );
	lv_obj_t *txt = lv_label_create( btn );
	lv_obj_set_pos( btn, 20, 20);
	lv_label_set_text( txt, "RESET" );
	lv_obj_add_event_cb(btn, btn_cb, LV_EVENT_CLICKED, NULL);

	lv_timer_create(update_arc_cb, 1000, arc_1);
}

test( lv_scr_act() ); 

Kind Regards,

Pete

1 Like

Hi Pete,

Thank you very much for the detailed response. I had managed to get something similar looking (well, almost similar :slight_smile: ), but was having trouble on the placement of the code within the file structure.

I will give this a try and let you know.

Again, thanks for your help, very much appreciated.

Regards

Damien

1 Like

Hi Pete,

Works awesome. Thanks

Regards

Damien

1 Like

No problem, you’re welcome…
:slight_smile:

Hello again Pete,

Sorry to bother you but I have a quick follow-up question if you don’t mind.

The timer is counting down in 2’s. I s this normal or am I missing something really obvious?

Regards

Damien

Hi @Damien_Norris ,

No problem, I am not sure why that would be. It sounds like the arc call-back is being called twice or something…

If you can print to a console I would put a line in the update_arc_cb() as follows:

static void update_arc_cb( lv_timer_t *timer ) {

	if( timer_value ) lv_arc_set_value( arc_1, --timer_value );
    printf( "tick:%d\r\n", lv_tick_get());
	update_knob_label( klabel );
}
Output from console:
tick:1152
tick:2156
tick:3158
tick:4162
tick:5164
tick:6165
tick:7167
tick:8172
tick:9173
tick:10174
tick:11179
tick:12179
tick:13184

As you can see its approximately 1000 ticks between each call on my simulation.
If the call-back is being executed twice you will probably see a double entry with a small number of ticks between followed by another double entry approximately 1000 ticks later if that makes sense?

Simulator Screen:
timer_anim
If we can establish the call-back is called twice maybe you can share your current code and I’ll take a look…

Kind Regards,

Pete

1 Like

Hi @Damien_Norris ,

Another little suggestion is when you create the timer save the returned pointer to a static variable then you can reset the timer so it counts the first second correctly:

#define COUNT		10

static lv_obj_t		*arc_1;
static lv_obj_t		*klabel;
static uint32_t		timer_value = 0;
static lv_timer_t 	*arc_timer;

static void update_knob_label( lv_obj_t *label ) {

	/*Rotate the label to the current position of the arc*/
	lv_label_set_text_fmt(label, "%d", timer_value );
	lv_arc_rotate_obj_to_angle(arc_1, label, 35);
}

static void update_arc_cb( lv_timer_t *timer ) {

	if( timer_value ) lv_arc_set_value( arc_1, --timer_value );
	printf( "tick:%d\r\n", lv_tick_get());
	fflush(stdout);
	update_knob_label( klabel );
}

static void btn_cb( lv_event_t *e ) {

	timer_value = COUNT;
	lv_arc_set_value(arc_1, COUNT);
	lv_timer_reset(arc_timer);
	update_knob_label( klabel );
}

void test( lv_obj_t *parent ) {

	klabel = lv_label_create( parent );
	lv_obj_set_style_text_color( klabel, lv_palette_main(LV_PALETTE_BLUE), LV_PART_MAIN);
	arc_1 = lv_arc_create(parent);
	timer_value = COUNT;

	lv_arc_set_mode(arc_1, LV_ARC_MODE_NORMAL);
	lv_arc_set_range(arc_1, 0, COUNT);
	lv_arc_set_angles(arc_1, 45, 315);
	lv_obj_set_style_arc_rounded(arc_1, 0, LV_PART_INDICATOR|LV_STATE_DEFAULT);
	lv_obj_set_style_arc_rounded(arc_1, 0, LV_STATE_DEFAULT);
	lv_obj_set_pos(arc_1,  120, 120);
	lv_obj_set_size(arc_1, 100, 100);
	lv_arc_set_value( arc_1, COUNT );
	update_knob_label( klabel );

	lv_obj_t *btn = lv_btn_create( parent );
	lv_obj_t *txt = lv_label_create( btn );
	lv_obj_set_pos( btn, 20, 20);
	lv_label_set_text( txt, "RESET" );
	lv_obj_add_event_cb(btn, btn_cb, LV_EVENT_CLICKED, NULL);

	arc_timer = lv_timer_create(update_arc_cb, 1000, arc_1);
}

Kind Regards,

Pete

1 Like

Hi Pete,

Thank you very much for the rapid response, always appreciated. Also thanks for the insights, it reminded me I’m an idiot :slight_smile:

I really only used the code you gave me to get it working, so no updates. However, I was reminded that I use one of two methods for testing things out for the Core2/Arduino/LVGL/Squareline Studio. In this instance I was using the same kind of setup that bareboat-necessities uses for their excellent M5Stack Tough (ESP-32) Sailing Instruments Display. I did contact them for permission to use their framework for working with M5Core2/Tough.

It requires that you build screens using the following format -

void init_uiTestScreen() {
	uiTestScreen.screen = lv_obj_create(NULL);  // Creates a Screen object
	uiTestScreen.init_cb = lv_ui_test_display;
	uiTestScreen.update_cb = ui_test_update_cb;
}

By not commenting out the last line I was calling the timer twice, as soon as I did it worked perfectly. Another solution would be to put the timer in the update function.

Thanks again for your help

Regards

Damien

1 Like

Hi @Damien_Norris ,

No worries, I am sure your not any idiot, but if it’s any consolation I am an idiot frequently! :laughing:

I’m glad you found the problem!

Cheers,

Pete

1 Like

Thank you so much for this info!

1 Like

Hi Pete,

Just a quick one. I have been trying to get this to work with Squareline Studio generated files, but not having any luck with passing the COUNT variable between screens - everything else work fine.

Rather than declaring #define COUNT 10, I create an extern int COUNT in ui.h and int COUNT in ui.c and providing I either set the value of COUNT or put a number in lv_arc_set_range() it appears to work fine. However, when I try to set the value by passing a value from a roller the arc doesn’t countdown, but the label does. It’s driving me mad! Would I need to use memset() to pass values between screens or am I being stupid again?

Can you see anything obvious I am missing?

Regards

Damien

Hi @Damien_Norris ,

I am not familiar with Squareline Studio to be honest but hopefully you should be able to work with this…

Here is a function that should work for you:

void start_timer_with_count( uint32_t count) {

	timer_value = count;
    lv_arc_set_range(arc_1, 0, count);
	lv_arc_set_value(arc_1, count);
	lv_timer_reset(arc_timer);
	update_knob_label( klabel );
}

So when you want to set the timer going for say 60 seconds just call this function as follows:

start_timer_with_count( 60 ) ;

I think this is what you are trying to achieve, if I have mis-understood, my apologies…

Here is the original example complete, but reworked with this method:

static lv_obj_t		*arc_1;
static lv_obj_t		*klabel;
static uint32_t		timer_value = 0;
static lv_timer_t 	*arc_timer;

static void update_knob_label( lv_obj_t *label ) {

	/*Rotate the label to the current position of the arc*/
	lv_label_set_text_fmt(label, "%d", timer_value );
	lv_arc_rotate_obj_to_angle(arc_1, label, 35);
}

static void update_arc_cb( lv_timer_t *timer ) {

	if( timer_value ) lv_arc_set_value( arc_1, --timer_value );
	printf( "tick:%d\r\n", lv_tick_get());
	fflush(stdout);
	update_knob_label( klabel );
}

void start_timer_with_count( uint32_t count) {

	timer_value = count;
    lv_arc_set_range(arc_1, 0, count);
	lv_arc_set_value(arc_1, count);
	lv_timer_reset(arc_timer);
	update_knob_label( klabel );
}

static void btn_cb( lv_event_t *e ) {

	start_timer_with_count( 60 );  // Start the timer with 60 seconds when button is pressed.
}

void test( lv_obj_t *parent ) {

	klabel = lv_label_create( parent );
	lv_obj_set_style_text_color( klabel, lv_palette_main(LV_PALETTE_BLUE), LV_PART_MAIN);
	arc_1 = lv_arc_create(parent);

	lv_arc_set_mode(arc_1, LV_ARC_MODE_NORMAL);
	lv_arc_set_angles(arc_1, 45, 315);
	lv_obj_set_style_arc_rounded(arc_1, 0, LV_PART_INDICATOR|LV_STATE_DEFAULT);
	lv_obj_set_style_arc_rounded(arc_1, 0, LV_STATE_DEFAULT);
	lv_obj_set_pos(arc_1,  120, 120);
	lv_obj_set_size(arc_1, 100, 100);
	update_knob_label( klabel );

	lv_obj_t *btn = lv_btn_create( parent );
	lv_obj_t *txt = lv_label_create( btn );
	lv_obj_set_pos( btn, 20, 20);
	lv_label_set_text( txt, "60 Secs" );
	lv_obj_add_event_cb(btn, btn_cb, LV_EVENT_CLICKED, NULL);

	arc_timer = lv_timer_create(update_arc_cb, 1000, arc_1);

	start_timer_with_count( 10 );  // Make the timer start with 10 seconds at startup
}

I hope that makes sense…

If you have trouble integrating it post back with some code and I will take a look at it again a make some suggestions if necessary.

Kind Regards,

Pete

1 Like

Hi Pete,

Thank you very much for the reply. I’ll see if I can get it to as I would like and het back to you.

Regards

Damien

1 Like

Why you dont use anim for this?
I use this for meter as counter , but you can use it for slider or arc…

    lv_anim_t a;
    lv_anim_init(&a);
    lv_anim_set_exec_cb(&a, set_value);
    /*Set a callback to call when animation is ready.*/
    lv_anim_set_ready_cb(&a, (lv_anim_ready_cb_t)ready_cbmeter);

    lv_anim_set_values(&a, 100, 0);  // values to anim...
    lv_anim_set_repeat_delay(&a, 0);
    lv_anim_set_playback_delay(&a, 0);
    lv_anim_set_repeat_count(&a, 0);

    lv_anim_set_time(&a, TIMECOUNT);
    lv_anim_set_playback_time(&a, 0);
    lv_anim_set_var(&a, indic1);
    lv_anim_start(&a);

1 Like

Hello Marian_M,

Thank you very much for your input. Unfortunately, I am very new to LVGL and just trying to see what I can get working and how. Sadly, for me, I have no idea how or where to use anim but I will certainly look into it and see if I can utilise your code.

Kind Regards

Damien

Hi Pete,

Thanks for this, I can now pass values from a roller to the arc on a separate screen. I am still perplexed as to why I couldn’t pass variables the normal way, but to paraphrase an old saying ‘if it works, don’t try to fix it’ :slight_smile:

Regards

Damien

1 Like