Chart display floats /temperature values with decimal places)

Description

Howto to display temperature values with decimal places in the chart widget=

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

ESP32

What LVGL version are you using?

8.2

What do you want to achieve?

Chart widget: Displaying temperature values with 1 decimal place on the y axis for example 20.0 to 20.9

What have you tried so far?

tried to multiply by 10 and add values, but how to scale the values

Code to reproduce

Screenshot and/or video

Hi @steff ,

Here is a complete example which logs a random temperature every minute for a 24 hour period. It also shows you how to set the ticks on the axis as this can be tricky if you haven’t seen it before. If you paste this in the simulator and call the make_chart() function it will update every 250ms where 250ms is equivalent to 1 minute of time on the chart. Here is the code:

#define NUM_HIST_X_LABELS	25
#define NUM_HIST_Y_LABELS	13
#define MINUTES_IN_A_DAY	1440
#define CHART_HEIGHT	410
#define CHART_WIDTH		610

static char 				*history_x_buf[NUM_HIST_X_LABELS];
static char 				*history_y_buf[NUM_HIST_Y_LABELS];
static lv_obj_t				*chart_history;
static lv_chart_series_t	*series_history;
static lv_coord_t			history[MINUTES_IN_A_DAY];


static void hist_chart_event_cb(lv_event_t * e) {

	lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);
	if(!lv_obj_draw_part_check_type(dsc, &lv_chart_class, LV_CHART_DRAW_PART_TICK_LABEL)/* || (dsc->value > 4)*/) return;
	static int32_t idx = NUM_HIST_Y_LABELS-1;

	if(dsc->id == LV_CHART_AXIS_PRIMARY_Y && dsc->text) {
		lv_snprintf(dsc->text, dsc->text_length, "%s", history_y_buf[idx]);
		if( --idx == -1 ) idx = NUM_HIST_Y_LABELS-1;
	} else if(dsc->id == LV_CHART_AXIS_PRIMARY_X && dsc->text) {
		lv_snprintf(dsc->text, dsc->text_length, "%s", history_x_buf[dsc->value]);
	}
}

static double random_temperature( void ) {

	double range = -120;

	range *= ((double)rand()/(double)(RAND_MAX));
	range += 100; // generate random temperature between -20 and 100
	printf( "temperature:%f\n", range);
	fflush(stdout);
	return( range );
}

static void update_chart( lv_timer_t * timer ) {

	static uint16_t idx = 0;

	lv_coord_t chart_value = (lv_coord_t)((random_temperature()+20) * 10); // get a random temperature and scale it to our chart
	printf( "scaled for chart:%d\n", chart_value);
	fflush(stdout);

	lv_chart_set_value_by_id(chart_history, series_history, idx % MINUTES_IN_A_DAY , chart_value );
	idx++;
}

static void make_chart( void ) {

	double			ticks;
	int32_t			i_pnt;

	chart_history = lv_chart_create(lv_scr_act());
	lv_obj_set_style_text_font (chart_history, &lv_font_roboto_8, LV_PART_MAIN | LV_STATE_DEFAULT);
	lv_obj_set_size(chart_history, CHART_WIDTH, CHART_HEIGHT);
	lv_obj_align(chart_history, LV_ALIGN_BOTTOM_MID, 28, -50);
	lv_chart_set_type(chart_history, LV_CHART_TYPE_LINE);
	lv_chart_set_range(chart_history, LV_CHART_AXIS_PRIMARY_Y, 0, 1200 );
	lv_chart_set_div_line_count(chart_history, 13, 25);
	lv_chart_set_update_mode(chart_history, LV_CHART_UPDATE_MODE_CIRCULAR);	//LV_CHART_UPDATE_MODE_SHIFT
	lv_chart_set_axis_tick(chart_history, LV_CHART_AXIS_PRIMARY_X, 10, 5, 25, 1, true, 50);
	lv_chart_set_axis_tick(chart_history, LV_CHART_AXIS_PRIMARY_Y, 10, 5, 13, 5, true, 30);
	/*Add a data series*/
	series_history = lv_chart_add_series(chart_history, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
	lv_obj_set_style_line_width(chart_history, 1, LV_PART_INDICATOR);
	lv_obj_set_style_size(chart_history, 0, LV_PART_INDICATOR);
	lv_chart_set_ext_y_array(chart_history, series_history, history);
	lv_chart_set_point_count(chart_history, sizeof(history)/sizeof(history[0]));
	lv_obj_update_layout(chart_history);
	// These never change so just set them the first time...
	i_pnt = 0;
	int32_t i = NUM_HIST_Y_LABELS-1;
	char buf[40];
	for( ticks=100; ticks >= -30; ticks-= 10 ) {
		i_pnt = snprintf( buf, sizeof(buf), "%6.1f", ticks  );
		history_y_buf[i] = malloc(i_pnt);
		strcpy( history_y_buf[i--], buf );

	}
	for( int iticks = 0; iticks < NUM_HIST_X_LABELS; iticks++ ) {
			i_pnt = snprintf( buf, sizeof(buf), "%02d\n", iticks );
			history_x_buf[iticks] = malloc(i_pnt);
			if( iticks < 24 ) strcpy( history_x_buf[iticks], buf );
			else history_x_buf[iticks][0] = 0;// Set Null string for last tick
	}
    lv_obj_add_event_cb(chart_history, hist_chart_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);
	lv_timer_create((void*)update_chart, 250, NULL );  // Update the chart periodically

}

Hopefully you will be able to see how to do things from this example.

Kind Regards,

Pete

Thank you for your effort, in fact it is essentially what I wanted to do.

But… Unfortunately the program crashes on my hardware and in the simulator.

char buf[40];
for( ticks=100; ticks >= -30; ticks-= 10 ) {
  i_pnt = snprintf( buf, sizeof(buf), "%6.1f", ticks  );
  history_y_buf[i] = malloc(i_pnt);
  strcpy( history_y_buf[i--], buf );
}

the first strcpy fails?

strange…

for( ticks=100; ticks > -30; ticks-= 10 ) {
	i_pnt = snprintf( buf, sizeof(buf), "%6.1f", ticks  );
	printf ("i_pnt = %d, buf = '%s'\n", i_pnt, buf);

gives me for the first iteration
i_pnt = 6, buf = ’ 100.0’

notice the ‘blank’ before the ‘100’!!!
In the next step, not enough bytes are reserved by ‘malloc’.

Same problem for the y-axis

I ‘fixed’ it by ‘malloc(8)’

ticks >= -30; results in ‘out of bounds’ therefore I changed to ‘ticks > -30’

Pete, thank you for the example!!!

Hi @steff ,

Sorry I think I made a mistake, the malloc should be i_pnt +1 to allow for the terminating NULL… My bad!

I am glad it has enabled you to make progress!

Kind Regards,

Pete