#define BAR_NUM 24 typedef struct { int32_t max_value; lv_obj_t* chart; int32_t data_buf[BAR_NUM]; lv_obj_t* scale_right; int32_t obj_line_x; } ChartData; static ChartData chart_data; /** * Adjusts the value `v` to align with the nearest lower multiple of `step_floor_v`. * If `v` is less than or equal to 0, it will be set to `step_floor_v`. * Otherwise, it will be rounded down to the nearest multiple of `step_floor_v`. * * @param v The value to be adjusted. * @param step_floor_v The step size to align the value to. * @return The adjusted value. */ static void floor_data_to(int* v, int step_floor_v) { if (v == NULL || step_floor_v == 0) { // Handle invalid input return; } *v = step_floor_v + (((*v <= 0) ? 0 : (*v - 1)) / step_floor_v) * step_floor_v; } /** * Scans the buffer `buf` for the maximum value and stores it in `_p_max`. * @param buf The data buffer to scan. * @param points_cnt The number of data points. * @param _p_max Pointer to store the maximum value found. */ static void points_scan_max(lv_coord_t* buf, uint32_t points_cnt, lv_coord_t* _p_max) { lv_coord_t max_v = LV_COORD_MIN; if (_p_max) { for (uint32_t i = 0; i < points_cnt; i++) { if (buf[i] > max_v) { max_v = buf[i]; } } *_p_max = max_v; } } /** * Generates random data and stores it in the provided buffer `buf`. * @param buf The buffer to store random data. * @param cnt The number of random data points to generate. * @param max_val The maximum value for random data generation. */ static void _generate_random_data(int32_t* buf, uint16_t cnt, int32_t max_val) { for (int i = 0; i < cnt; i++) { buf[i] = lv_rand(0, max_val); } } /** * Event callback for the "Random" button to generate random data * and update the chart and scale. */ static void rand_button_cb(lv_event_t* e) { // Generate new random data and update chart range lv_chart_series_t* ser1 = lv_event_get_user_data(e); _generate_random_data(chart_data.data_buf, BAR_NUM, 1000); points_scan_max(chart_data.data_buf, BAR_NUM, &chart_data.max_value); floor_data_to(&chart_data.max_value, 10); // Round down max_value to the nearest multiple of 10 // Update chart's Y-axis range and data series lv_chart_set_range(chart_data.chart, LV_CHART_AXIS_PRIMARY_Y, 0, chart_data.max_value); lv_chart_set_ext_y_array(chart_data.chart, ser1, chart_data.data_buf); // Update the right scale range lv_scale_set_range(chart_data.scale_right, 0, chart_data.max_value); } /** * Event callback for slider value change. Moves the vertical line based on the slider value. */ static void slider_event_cb(lv_event_t* e) { lv_obj_t* slider = lv_event_get_target_obj(e); lv_obj_t* obj_line = lv_event_get_user_data(e); lv_area_t chart_coords; lv_obj_get_coords(chart_data.chart, &chart_coords); int32_t value = lv_slider_get_value(slider); // Update line position based on slider value lv_obj_set_x(obj_line, chart_data.obj_line_x + value); // Simulate press/release event based on line position if (chart_data.obj_line_x + value < chart_coords.x2 - 1) lv_obj_send_event(chart_data.chart, LV_EVENT_PRESSED, NULL); else lv_obj_send_event(chart_data.chart, LV_EVENT_RELEASED, NULL); } /** * Event callback for chart. Handles drawing events and value display. */ static void chart_event_cb(lv_event_t* e) { lv_event_code_t code = lv_event_get_code(e); lv_obj_t* chart = lv_event_get_target(e); lv_obj_t* step_value_label = lv_event_get_user_data(e); int32_t value = 0; int sum = 0; if (code == LV_EVENT_DRAW_TASK_ADDED) { // Handle custom drawing for pressed points lv_draw_task_t* draw_task = lv_event_get_draw_task(e); lv_draw_dsc_base_t* base_dsc = lv_draw_task_get_draw_dsc(draw_task); if (base_dsc->part == LV_PART_ITEMS && lv_chart_get_pressed_point(chart) == base_dsc->id2) { lv_draw_fill_dsc_t* fill_dsc = lv_draw_task_get_draw_dsc(draw_task); fill_dsc->color = lv_color_make(100, 187, 93); // Highlight color } } else if (code == LV_EVENT_PRESSED) { int32_t id = lv_chart_get_pressed_point(chart); const lv_chart_series_t* ser = lv_chart_get_series_next(chart, NULL); int32_t* y_array = lv_chart_get_y_array(chart, ser); value = y_array[id]; // Display individual bar value if (value != 0) lv_label_set_text_fmt(step_value_label, "%d", value); } else if (code == LV_EVENT_RELEASED) { int32_t id = lv_chart_get_pressed_point(chart); const lv_chart_series_t* ser = lv_chart_get_series_next(chart, NULL); int32_t* y_array = lv_chart_get_y_array(chart, ser); // Calculate and display the sum of all bar values for (int i = 0; i < BAR_NUM; i++) { sum += y_array[i]; } if (sum != 0) lv_label_set_text_fmt(step_value_label, "%d", sum); } } /** * Main function to create a chart with various UI elements. */ void lv_example_grid_3(void) { // Column and row descriptions for grid layout static int32_t col_dsc[] = { LV_GRID_FR(8), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST }; static int32_t row_dsc[] = { LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(8), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST }; // Create a container with grid layout lv_obj_t* cont = lv_obj_create(lv_screen_active()); lv_obj_center(cont); // Center the container lv_obj_set_size(cont, 400, 300); // Set container size lv_obj_remove_flag(cont, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_style_pad_left(cont, 20, LV_PART_MAIN); lv_obj_set_grid_dsc_array(cont, col_dsc, row_dsc); // Apply the grid description // Create UI elements inside the container lv_obj_t* step_title_label = lv_label_create(cont); lv_label_set_text(step_title_label, "step value"); lv_obj_set_grid_cell(step_title_label, LV_GRID_ALIGN_CENTER, 0, 1, LV_GRID_ALIGN_STRETCH, 0, 1); lv_obj_t* step_value_label = lv_label_create(cont); lv_label_set_text(step_value_label, "--"); lv_obj_set_grid_cell(step_value_label, LV_GRID_ALIGN_CENTER, 0, 1, LV_GRID_ALIGN_STRETCH, 1, 1); // Create a chart with bar type chart_data.chart = lv_chart_create(cont); lv_chart_set_type(chart_data.chart, LV_CHART_TYPE_BAR); lv_chart_set_div_line_count(chart_data.chart, 5, 0); lv_obj_set_style_pad_ver(chart_data.chart, 0, LV_PART_MAIN); lv_chart_set_point_count(chart_data.chart, BAR_NUM); lv_obj_set_style_radius(chart_data.chart, 0, LV_PART_MAIN); lv_obj_set_style_border_width(chart_data.chart, 0, LV_PART_MAIN); lv_obj_set_grid_cell(chart_data.chart, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_STRETCH, 2, 1); lv_chart_series_t* ser1 = lv_chart_add_series(chart_data.chart, lv_color_make(162, 214, 156), LV_CHART_AXIS_PRIMARY_Y); // Generate initial random data and set chart range _generate_random_data(chart_data.data_buf, BAR_NUM, 1000); points_scan_max(chart_data.data_buf, BAR_NUM, &chart_data.max_value); floor_data_to(&chart_data.max_value, 10); lv_chart_set_range(chart_data.chart, LV_CHART_AXIS_PRIMARY_Y, 0, chart_data.max_value); lv_chart_set_ext_y_array(chart_data.chart, ser1, chart_data.data_buf); lv_obj_add_event_cb(chart_data.chart, chart_event_cb, LV_EVENT_ALL, step_value_label); lv_obj_add_flag(chart_data.chart, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS); lv_obj_remove_flag(chart_data.chart, LV_OBJ_FLAG_CLICKABLE); lv_obj_t* rand_button = lv_button_create(cont); lv_obj_set_grid_cell(rand_button, LV_GRID_ALIGN_CENTER, 1, 1, LV_GRID_ALIGN_START, 0, 1); lv_obj_set_style_bg_color(rand_button, lv_palette_main(LV_PALETTE_GREEN), LV_PART_MAIN); lv_obj_add_event_cb(rand_button, rand_button_cb, LV_EVENT_SHORT_CLICKED, ser1); lv_obj_t* rand_label = lv_label_create(rand_button); lv_label_set_text(rand_label, "rand"); // Create scale on the right side chart_data.scale_right = lv_scale_create(cont); lv_scale_set_mode(chart_data.scale_right, LV_SCALE_MODE_VERTICAL_RIGHT); lv_scale_set_total_tick_count(chart_data.scale_right, 5); lv_scale_set_major_tick_every(chart_data.scale_right, 1); lv_obj_set_style_line_opa(chart_data.scale_right, LV_OPA_TRANSP, LV_PART_MAIN); lv_obj_set_style_line_opa(chart_data.scale_right, LV_OPA_TRANSP, LV_PART_INDICATOR); lv_obj_set_grid_cell(chart_data.scale_right, LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_STRETCH, 2, 1); lv_scale_set_range(chart_data.scale_right, 0, chart_data.max_value); // Create scale at the bottom (time labels) lv_obj_t* scale_bottom = lv_scale_create(cont); lv_scale_set_mode(scale_bottom, LV_SCALE_MODE_HORIZONTAL_BOTTOM); lv_scale_set_total_tick_count(scale_bottom, 5); lv_scale_set_major_tick_every(scale_bottom, 1); lv_obj_set_grid_cell(scale_bottom, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_STRETCH, 3, 1); lv_obj_set_style_pad_hor(scale_bottom, lv_chart_get_first_point_center_offset(chart_data.chart), LV_PART_MAIN); lv_obj_set_style_line_opa(scale_bottom, LV_OPA_TRANSP, LV_PART_MAIN); lv_obj_set_style_line_opa(scale_bottom, LV_OPA_TRANSP, LV_PART_INDICATOR); static const char* time[] = { "00:00", "06:00", "12:00", "18:00", "24:00", NULL }; lv_scale_set_text_src(scale_bottom, time); // Create slider to move the vertical line lv_area_t chart_coords; lv_obj_get_coords(chart_data.chart, &chart_coords); lv_obj_t* slider = lv_slider_create(cont); lv_obj_set_style_bg_opa(slider, LV_OPA_TRANSP, LV_PART_INDICATOR); lv_obj_set_style_bg_opa(slider, LV_OPA_TRANSP, LV_PART_MAIN); lv_obj_set_style_bg_color(slider, lv_color_make(162, 214, 156), LV_PART_KNOB); lv_obj_set_style_pad_left(slider, lv_chart_get_first_point_center_offset(chart_data.chart), LV_PART_MAIN); lv_obj_set_grid_cell(slider, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_STRETCH, 4, 1); lv_slider_set_range(slider, 0, chart_coords.x2 - chart_coords.x1 - lv_obj_get_style_pad_left(chart_data.chart, LV_PART_MAIN)); // Create the vertical line to show position lv_obj_t* obj_line = lv_obj_create(lv_screen_active()); lv_obj_remove_style_all(obj_line); lv_obj_set_size(obj_line, 1, 200); lv_obj_set_style_bg_color(obj_line, lv_color_make(162, 214, 156), LV_PART_MAIN); lv_obj_set_style_bg_opa(obj_line, LV_OPA_80, LV_PART_MAIN); chart_data.obj_line_x = chart_coords.x1 + lv_obj_get_style_pad_left(chart_data.chart, LV_PART_MAIN); lv_obj_set_pos(obj_line, chart_data.obj_line_x, chart_coords.y1); lv_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, obj_line); }