Chart (lv_chart)

Overview

Charts Widget are used to visualize data.

Charts have:

  • 0 or more background division lines (horizontal and vertical),

  • 4 internal axes, any of which can be used to specify scale for a data series,

  • a point_count (default 10) that applies to all data series added,

  • 0 or more data series (you can add or remove them at any time),

  • 0 or more cursors (you can add or remove them at any time),

  • update mode (modifies behavior of lv_chart_set_next_value() if you use it).

Charts can:

  • display flexible axes with ticks and text

  • show precise locations of points or other locations on chart with cursors

  • show or hide individual data series

  • show or hide individual data points

  • show or hide cursors

  • scroll and zoom

Chart Basics

Each chart has the following attributes (over and above attributes found in all Widgets):

Type (governs how a chart's data series are drawn)
  • Can be LINE (default), BAR, SCATTER, or none.

  • You can change the chart's type at any point during it's life.

Horizontal and Vertical division lines
  • default 3 and 5 respectively

  • can be any non-negative value including 0

2 Y axes and 2 X axes (the latter are used with SCATTER charts)
  • All 4 axes come with each chart automatically (they do not have to be created).

  • Their default ranges are [0..100]. If you need a different range, set it before chart is drawn.

  • You "use" an axis by associating it with a data series, which happens when the data series is created (more on this below). More than one data series can be associated with each axis.

Point count (number of data points in all data series added to the chart)
  • default 10

  • If you provide your own data-value arrays, each array so provided must contain at least this number of values.

  • For LINE- and BAR-charts, this is the number of points on the X axis.

  • LINE- and BAR-charts require only one data-value array to supply Y-values for each data point.

  • For SCATTER charts, this is the number of scatter-points in the data series.

  • SCATTER charts have separate data-value arrays for both X-values and Y-values.

Any number of data series
  • After a chart is created, it initially contains no data series. You have to add them.

  • You can add and remove data series at any time during a chart's life.

  • When a data series is created, it comes with pre-allocated values array(s) based on its chart type and point_count. (All chart types use an array of Y-values. SCATTER-type charts also use an array of X-values.). All Y-values so allocated are set to LV_CHART_POINT_NONE, which causes that point to be hidden.

  • To get points to be drawn on the chart, you must set their Y-values to something other than LV_CHART_POINT_NONE.

  • You can hide a point by setting its Y-value to LV_CHART_POINT_NONE.

  • If desired, you can tell a data series to instead use a value array you provide. If you do:

    • Pre-allocated value arrays are automatically freed.

    • That data series will continue to use your array from that time onward.

    • The values in your array must remain available through the life of that data series.

    • You must ensure each array provided contains at least point_count int32_t elements.

    • Management of the life any value arrays you provide is up to you.

Any number of cursors
  • After a chart is created, it initially contains no cursors. You have to add them if you want to use them.

  • You can add, show, hide or remove cursors at any time during a chart's life.

Update mode

Chart layers

When a chart is drawn, certain things appear on top of other things, in this order, from back to front:

  • The chart's background (with optional division lines)

  • Each data series:

    • Earliest data series added appears on top.

    • For a SCATTER chart, within each series, points later in the sequence will appear on top of points earlier in the sequence when there is overlap.

  • Each cursor (if there are any):

    • The most recent cursor added appears on top.

Parts and Styles

  • LV_PART_MAIN The background of the chart. Uses the typical background and line style properties (for division lines). Padding makes the series area smaller. For BAR charts pad_column sets the space between bars in the same data series.

  • LV_PART_SCROLLBAR A scrollbar used if the chart is zoomed. See Base Widget's documentation for details.

  • LV_PART_ITEMS Refers to the LINE or BAR data series.

    • LINE chart: line properties are used by lines. width, height, bg_color and radius are used to set the appearance of points on the line.

    • Bar chart: The typical background properties are used to style the bars. pad_column sets the space between columns in the same data series.

  • LV_PART_INDICATOR Refers to points on LINE- and SCATTER-charts (small circles or squares [with possibly-rounded corners]).

  • LV_PART_CURSOR Line properties are used to style cursors. width, height, bg_color and radius are used to set the appearance of the cursor's "point" showing its location. If either width or height are set to 0, only the cursor's lines are drawn.

Details

Chart type

A chart can be one of the following types:

Charts start their life as LINE charts. You can change a chart's type with lv_chart_set_type(chart, LV_CHART_TYPE_...).

Data series

You can add any number of data series to a chart by using

lv_chart_add_series(chart, color, axis).

This allocates (and returns a pointer to) an lv_chart_series_t structure which remembers the color and axis you specified, and comes pre-allocated with an array of chart->point_cnt int32_t Y-values, all set to LV_CHART_POINT_NONE. (A SCATTER chart also comes with a pre-allocated array of the same number of X-values.)

axis specifies which axis is used to scale its values, and may be one of the following:

When adding a data series to a SCATTER chart, bit-wise OR your selected Y axis (primary or secondary) with one of the X-axis values.

If you wish to have the chart use your own Y-value array instead of the one provided, you can do so with

lv_chart_set_ext_y_array(chart, series, value_array).

You are responsible for ensuring the array you provide contains at least chart->point_cnt elements in it.

value_array should look like this: int32_t * value_array[num_points]. Only the array's pointer is saved in the series so its contents need to remain available for the life of the series, i.e. the array needs to be global, static or dynamically allocated.

Note

Call lv_chart_refresh(chart) when a chart's data has changed to signal that the chart should be re-rendered next time a display refresh occurs. You do not need to do this if you are using the provided value array(s) and setting values with lv_chart_set_...value_...() functions. See below for more information about these functions.

A pointer to the Y-value array of a series can be obtained with lv_chart_get_y_array(chart, series). This is true whether you are using the provided Y-value array or provided your own.

For SCATTER-type charts,

can be used as well.

Modifying data

You have several options to set the Y-values for a data series:

  1. Set the values programmatically in the array like ser1->points[3] = 7 and refresh the chart with lv_chart_refresh(chart).

  2. Use lv_chart_set_value_by_id(chart, series, id, value) where id is the zero-based index of the point you wish to update.

  3. Use lv_chart_set_next_value(chart, series, value). (See Update modes below.)

  4. Set all points to a single Y-value with lv_chart_set_all_values(chart, series, value).

Use LV_CHART_POINT_NONE as value to make the library skip drawing that point, column, or scatter-point.

For SCATTER-type charts,

can be used as well.

Update modes

lv_chart_set_next_value() can behave in two ways depending on update mode:

The update mode can be changed with lv_chart_set_update_mode(chart, LV_CHART_UPDATE_MODE_...).

Number of points

The number of points in the series can be modified by lv_chart_set_point_count(chart, point_num). The default value is 10. Note: this affects the number of points processed when an external value array is assigned to a series, so you also need to be sure any external array so provided contains at least point_num elements.

Handling large numbers of points

On LINE charts, if the number of points is greater than the pixels horizontally, the Chart will draw only vertical lines to make the drawing of large amount of data effective. If there are, let's say, 10 points to a pixel, LVGL searches the smallest and the largest value and draws a vertical lines between them to ensure no peaks are missed.

Vertical range

You can specify the minimum and maximum values in Y-direction with lv_chart_set_range(chart, axis, min, max). axis can be LV_CHART_AXIS_PRIMARY_Y (left Y axis) or LV_CHART_AXIS_SECONDARY_Y (right Y axis).

The value of the points will be scaled proportionally. The default range is: 0..100.

Division lines

The number of horizontal and vertical division lines can be modified by lv_chart_set_div_line_count(chart, hdiv_num, vdiv_num). The default settings are 3 horizontal and 5 vertical division lines. If there is a visible border on a side and no padding on that side, the division line would be drawn on top of the border and in this case it is not drawn so as not to hide the chart border.

Override default start point for series

If you want a plot to start from a point other than the default which is point[0] of the series, you can set an alternative index with the function lv_chart_set_x_start_point(chart, series, id) where id is the new zero-based index position to start plotting from.

Note that LV_CHART_UPDATE_MODE_SHIFT also changes the start_point.

Tick marks and labels

With the help of Scale, vertical and horizontal scales can be added in a very flexible way. See the examples 2 below to learn more.

Zoom

To zoom the chart all you need to do is wrap it in a parent container and set the chart's width and/or height to a larger value. Doing this will cause the the chart to be scrollable in its parent — the parent container provides the scrollable "view window".

Cursor

A new cursor is initially given position LV_CHART_POINT_NONE which causes it to be hidden. To show the cursor, its location must be set by you programmatically using one of the functions below.

You can hide a cursor without removing it from the chart by using lv_chart_set_cursor_point() by passing LV_CHART_POINT_NONE as the point id.

A cursor can be added with lv_chart_cursor_t * c1 = lv_chart_add_cursor(chart, color, dir);. The possible values of dir are the enumeration values of lv_dir_t: LV_DIR_NONE/RIGHT/UP/LEFT/DOWN/HOR/VER/ALL or their bit-wise OR-ed values to tell the chart which direction(s) to draw its lines.

lv_chart_set_cursor_pos(chart, cursor, &point) sets the position of the cursor to an arbitrary point on the chart. &point is a pointer to an lv_point_t variable. E.g. lv_point_t point = {10, 20}. If the chart is scrolled, the cursor moves with it.

lv_chart_get_point_pos_by_id(chart, series, id, &point_out) gets the coordinate of a given point on the chart. This is useful to place the cursor on that data point.

lv_chart_set_cursor_point(chart, cursor, series, point_id) places the cursor on the specified data point on the chart. If the point's position changes (via a new value or via scrolling), the cursor moves with the point. See an example of using this function here.

Events

Further Reading

Learn more about Base-Widget Events emitted by all Widgets.

Learn more about Events.

Keys

No Keys are processed by Chart Widgets.

Further Reading

Learn more about Keys.

Example

Line Chart

#include "../../lv_examples.h"
#if LV_USE_CHART && LV_BUILD_EXAMPLES

/**
 * A very basic line chart
 */
void lv_example_chart_1(void)
{
    /*Create a chart*/
    lv_obj_t * chart;
    chart = lv_chart_create(lv_screen_active());
    lv_obj_set_size(chart, 200, 150);
    lv_obj_center(chart);
    lv_chart_set_type(chart, LV_CHART_TYPE_LINE);   /*Show lines and points too*/

    /*Add two data series*/
    lv_chart_series_t * ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_PRIMARY_Y);
    lv_chart_series_t * ser2 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_SECONDARY_Y);
    int32_t * ser2_y_points = lv_chart_get_y_array(chart, ser2);

    uint32_t i;
    for(i = 0; i < 10; i++) {
        /*Set the next points on 'ser1'*/
        lv_chart_set_next_value(chart, ser1, lv_rand(10, 50));

        /*Directly set points on 'ser2'*/
        ser2_y_points[i] = lv_rand(50, 90);
    }

    lv_chart_refresh(chart); /*Required after direct set*/
}

#endif

Axis ticks and labels with scrolling

#include "../../lv_examples.h"
#if LV_USE_CHART && LV_BUILD_EXAMPLES

/**
 * Use lv_scale to add ticks to a scrollable chart
 */
void lv_example_chart_2(void)
{
    /*Create a container*/
    lv_obj_t * main_cont = lv_obj_create(lv_screen_active());
    lv_obj_set_size(main_cont, 200, 150);
    lv_obj_center(main_cont);

    /*Create a transparent wrapper for the chart and the scale.
     *Set a large width, to make it scrollable on the main container*/
    lv_obj_t * wrapper = lv_obj_create(main_cont);
    lv_obj_remove_style_all(wrapper);
    lv_obj_set_size(wrapper, lv_pct(300), lv_pct(100));
    lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);

    /*Create a chart on the wrapper
     *Set it's width to 100% to fill the large wrapper*/
    lv_obj_t * chart = lv_chart_create(wrapper);
    lv_obj_set_width(chart, lv_pct(100));
    lv_obj_set_flex_grow(chart, 1);
    lv_chart_set_type(chart, LV_CHART_TYPE_BAR);
    lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, 0, 100);
    lv_chart_set_range(chart, LV_CHART_AXIS_SECONDARY_Y, 0, 400);
    lv_chart_set_point_count(chart, 12);
    lv_obj_set_style_radius(chart, 0, 0);

    /*Create a scale also with 100% width*/
    lv_obj_t * scale_bottom = lv_scale_create(wrapper);
    lv_scale_set_mode(scale_bottom, LV_SCALE_MODE_HORIZONTAL_BOTTOM);
    lv_obj_set_size(scale_bottom, lv_pct(100), 25);
    lv_scale_set_total_tick_count(scale_bottom, 12);
    lv_scale_set_major_tick_every(scale_bottom, 1);
    lv_obj_set_style_pad_hor(scale_bottom, lv_chart_get_first_point_center_offset(chart), 0);

    static const char * month[] = {"Jan", "Febr", "March", "Apr", "May", "Jun", "July", "Aug", "Sept", "Oct", "Nov", "Dec", NULL};
    lv_scale_set_text_src(scale_bottom, month);

    /*Add two data series*/
    lv_chart_series_t * ser1 = lv_chart_add_series(chart, lv_palette_lighten(LV_PALETTE_GREEN, 2), LV_CHART_AXIS_PRIMARY_Y);
    lv_chart_series_t * ser2 = lv_chart_add_series(chart, lv_palette_darken(LV_PALETTE_GREEN, 2), LV_CHART_AXIS_PRIMARY_Y);

    /*Set the next points on 'ser1'*/
    uint32_t i;
    for(i = 0; i < 12; i++) {
        lv_chart_set_next_value(chart, ser1, lv_rand(10, 60));
        lv_chart_set_next_value(chart, ser2, lv_rand(50, 90));
    }
    lv_chart_refresh(chart); /*Required after direct set*/
}

#endif

Show the value of the pressed points

#include "../../lv_examples.h"
#if LV_USE_CHART && LV_BUILD_EXAMPLES

static void 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);

    if(code == LV_EVENT_VALUE_CHANGED) {
        lv_obj_invalidate(chart);
    }
    if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
        int32_t * s = lv_event_get_param(e);
        *s = LV_MAX(*s, 20);
    }
    else if(code == LV_EVENT_DRAW_POST_END) {
        int32_t id = lv_chart_get_pressed_point(chart);
        if(id == LV_CHART_POINT_NONE) return;

        LV_LOG_USER("Selected point %d", (int)id);

        lv_chart_series_t * ser = lv_chart_get_series_next(chart, NULL);
        while(ser) {
            lv_point_t p;
            lv_chart_get_point_pos_by_id(chart, ser, id, &p);

            int32_t * y_array = lv_chart_get_y_array(chart, ser);
            int32_t value = y_array[id];

            /*Draw a rectangle above the clicked point*/
            lv_layer_t * layer = lv_event_get_layer(e);
            lv_draw_rect_dsc_t draw_rect_dsc;
            lv_draw_rect_dsc_init(&draw_rect_dsc);
            draw_rect_dsc.bg_color = lv_color_black();
            draw_rect_dsc.bg_opa = LV_OPA_50;
            draw_rect_dsc.radius = 3;

            lv_area_t chart_obj_coords;
            lv_obj_get_coords(chart, &chart_obj_coords);
            lv_area_t rect_area;
            rect_area.x1 = chart_obj_coords.x1 + p.x - 20;
            rect_area.x2 = chart_obj_coords.x1 + p.x + 20;
            rect_area.y1 = chart_obj_coords.y1 + p.y - 30;
            rect_area.y2 = chart_obj_coords.y1 + p.y - 10;
            lv_draw_rect(layer, &draw_rect_dsc, &rect_area);

            /*Draw the value as label to the center of the rectangle*/
            char buf[16];
            lv_snprintf(buf, sizeof(buf), LV_SYMBOL_DUMMY"$%d", value);

            lv_draw_label_dsc_t draw_label_dsc;
            lv_draw_label_dsc_init(&draw_label_dsc);
            draw_label_dsc.color = lv_color_white();
            draw_label_dsc.text = buf;
            draw_label_dsc.text_local = 1;
            draw_label_dsc.align = LV_TEXT_ALIGN_CENTER;
            lv_area_t label_area = rect_area;
            lv_area_set_height(&label_area, lv_font_get_line_height(draw_label_dsc.font));
            lv_area_align(&rect_area, &label_area, LV_ALIGN_CENTER, 0, 0);
            lv_draw_label(layer, &draw_label_dsc, &label_area);

            ser = lv_chart_get_series_next(chart, ser);
        }
    }
    else if(code == LV_EVENT_RELEASED) {
        lv_obj_invalidate(chart);
    }
}

/**
 * Show the value of the pressed points
 */
void lv_example_chart_3(void)
{
    /*Create a chart*/
    lv_obj_t * chart;
    chart = lv_chart_create(lv_screen_active());
    lv_obj_set_size(chart, 200, 150);
    lv_obj_center(chart);

    lv_obj_add_event_cb(chart, event_cb, LV_EVENT_ALL, NULL);
    lv_obj_refresh_ext_draw_size(chart);

    /*Zoom in a little in X*/
    //    lv_chart_set_scale_x(chart, 800);

    /*Add two data series*/
    lv_chart_series_t * ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
    lv_chart_series_t * ser2 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_PRIMARY_Y);
    uint32_t i;
    for(i = 0; i < 10; i++) {
        lv_chart_set_next_value(chart, ser1, lv_rand(60, 90));
        lv_chart_set_next_value(chart, ser2, lv_rand(10, 40));
    }
}

#endif

Recolor bars based on their value

#include "../../lv_examples.h"

#if LV_USE_CHART && LV_DRAW_SW_COMPLEX && LV_BUILD_EXAMPLES

static void draw_event_cb(lv_event_t * e)
{
    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) {
        return;
    }

    lv_draw_fill_dsc_t * fill_dsc = lv_draw_task_get_fill_dsc(draw_task);
    if(fill_dsc) {
        lv_obj_t * chart = lv_event_get_target(e);
        int32_t * y_array = lv_chart_get_y_array(chart, lv_chart_get_series_next(chart, NULL));
        int32_t v = y_array[base_dsc->id2];

        uint32_t ratio = v * 255 / 100;
        fill_dsc->color = lv_color_mix(lv_palette_main(LV_PALETTE_GREEN), lv_palette_main(LV_PALETTE_RED), ratio);
    }
}

/**
 * Recolor the bars of a chart based on their value
 */
void lv_example_chart_4(void)
{
    /*Create a chart1*/
    lv_obj_t * chart = lv_chart_create(lv_screen_active());
    lv_chart_set_type(chart, LV_CHART_TYPE_BAR);
    lv_chart_set_point_count(chart, 24);
    lv_obj_set_style_pad_column(chart, 2, 0);
    lv_obj_set_size(chart, 260, 160);
    lv_obj_center(chart);

    lv_chart_series_t * ser = lv_chart_add_series(chart, lv_color_hex(0xff0000), LV_CHART_AXIS_PRIMARY_Y);
    lv_obj_add_event_cb(chart, draw_event_cb, LV_EVENT_DRAW_TASK_ADDED, NULL);
    lv_obj_add_flag(chart, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS);

    uint32_t i;
    for(i = 0; i < 24; i++) {
        lv_chart_set_next_value(chart, ser, lv_rand(10, 90));
    }
}

#endif

Faded area line chart with custom division lines

#include "../../lv_examples.h"

#if LV_USE_CHART && LV_DRAW_SW_COMPLEX && LV_BUILD_EXAMPLES
#include "../../../lvgl_private.h"

static void hook_division_lines(lv_event_t * e);
static void add_faded_area(lv_event_t * e);
static void draw_event_cb(lv_event_t * e);

/**
 * Add a faded area effect to the line chart and make some division lines ticker
 */
void lv_example_chart_5(void)
{
    /*Create a chart*/
    lv_obj_t * chart = lv_chart_create(lv_screen_active());
    lv_chart_set_type(chart, LV_CHART_TYPE_LINE);   /*Show lines and points too*/
    lv_obj_set_size(chart, 200, 150);
    lv_obj_set_style_pad_all(chart, 0, 0);
    lv_obj_set_style_radius(chart, 0, 0);
    lv_obj_center(chart);

    lv_chart_set_div_line_count(chart, 5, 7);

    lv_obj_add_event_cb(chart, draw_event_cb, LV_EVENT_DRAW_TASK_ADDED, NULL);
    lv_obj_add_flag(chart, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS);

    lv_chart_series_t * ser = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);

    uint32_t i;
    for(i = 0; i < 10; i++) {
        lv_chart_set_next_value(chart, ser, lv_rand(10, 80));
    }
}

static void draw_event_cb(lv_event_t * e)
{
    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_draw_task_get_type(draw_task) == LV_DRAW_TASK_TYPE_LINE) {
        add_faded_area(e);

    }
    /*Hook the division lines too*/
    if(base_dsc->part == LV_PART_MAIN && lv_draw_task_get_type(draw_task) == LV_DRAW_TASK_TYPE_LINE) {
        hook_division_lines(e);
    }
}

static void add_faded_area(lv_event_t * e)
{
    lv_obj_t * obj = lv_event_get_target(e);
    lv_area_t coords;
    lv_obj_get_coords(obj, &coords);

    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);

    const lv_chart_series_t * ser = lv_chart_get_series_next(obj, NULL);
    lv_color_t ser_color = lv_chart_get_series_color(obj, ser);

    /*Draw a triangle below the line witch some opacity gradient*/
    lv_draw_line_dsc_t * draw_line_dsc = lv_draw_task_get_draw_dsc(draw_task);
    lv_draw_triangle_dsc_t tri_dsc;

    lv_draw_triangle_dsc_init(&tri_dsc);
    tri_dsc.p[0].x = draw_line_dsc->p1.x;
    tri_dsc.p[0].y = draw_line_dsc->p1.y;
    tri_dsc.p[1].x = draw_line_dsc->p2.x;
    tri_dsc.p[1].y = draw_line_dsc->p2.y;
    tri_dsc.p[2].x = draw_line_dsc->p1.y < draw_line_dsc->p2.y ? draw_line_dsc->p1.x : draw_line_dsc->p2.x;
    tri_dsc.p[2].y = LV_MAX(draw_line_dsc->p1.y, draw_line_dsc->p2.y);
    tri_dsc.bg_grad.dir = LV_GRAD_DIR_VER;

    int32_t full_h = lv_obj_get_height(obj);
    int32_t fract_uppter = (int32_t)(LV_MIN(draw_line_dsc->p1.y, draw_line_dsc->p2.y) - coords.y1) * 255 / full_h;
    int32_t fract_lower = (int32_t)(LV_MAX(draw_line_dsc->p1.y, draw_line_dsc->p2.y) - coords.y1) * 255 / full_h;
    tri_dsc.bg_grad.stops[0].color = ser_color;
    tri_dsc.bg_grad.stops[0].opa = 255 - fract_uppter;
    tri_dsc.bg_grad.stops[0].frac = 0;
    tri_dsc.bg_grad.stops[1].color = ser_color;
    tri_dsc.bg_grad.stops[1].opa = 255 - fract_lower;
    tri_dsc.bg_grad.stops[1].frac = 255;

    lv_draw_triangle(base_dsc->layer, &tri_dsc);

    /*Draw rectangle below the triangle*/
    lv_draw_rect_dsc_t rect_dsc;
    lv_draw_rect_dsc_init(&rect_dsc);
    rect_dsc.bg_grad.dir = LV_GRAD_DIR_VER;
    rect_dsc.bg_grad.stops[0].color = ser_color;
    rect_dsc.bg_grad.stops[0].frac = 0;
    rect_dsc.bg_grad.stops[0].opa = 255 - fract_lower;
    rect_dsc.bg_grad.stops[1].color = ser_color;
    rect_dsc.bg_grad.stops[1].frac = 255;
    rect_dsc.bg_grad.stops[1].opa = 0;

    lv_area_t rect_area;
    rect_area.x1 = (int32_t)draw_line_dsc->p1.x;
    rect_area.x2 = (int32_t)draw_line_dsc->p2.x - 1;
    rect_area.y1 = (int32_t)LV_MAX(draw_line_dsc->p1.y, draw_line_dsc->p2.y) - 1;
    rect_area.y2 = (int32_t)coords.y2;
    lv_draw_rect(base_dsc->layer, &rect_dsc, &rect_area);
}

static void hook_division_lines(lv_event_t * e)
{
    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);
    lv_draw_line_dsc_t * line_dsc = lv_draw_task_get_draw_dsc(draw_task);

    /*Vertical line*/
    if(line_dsc->p1.x == line_dsc->p2.x) {
        line_dsc->color  = lv_palette_lighten(LV_PALETTE_GREY, 1);
        if(base_dsc->id1 == 3) {
            line_dsc->width = 2;
            line_dsc->dash_gap = 0;
            line_dsc->dash_width = 0;
        }
        else {
            line_dsc->width = 1;
            line_dsc->dash_gap = 6;
            line_dsc->dash_width = 6;
        }
    }
    /*Horizontal line*/
    else {
        if(base_dsc->id1 == 2) {
            line_dsc->width  = 2;
            line_dsc->dash_gap  = 0;
            line_dsc->dash_width  = 0;
        }
        else {
            line_dsc->width = 2;
            line_dsc->dash_gap  = 6;
            line_dsc->dash_width  = 6;
        }

        if(base_dsc->id1 == 1  || base_dsc->id1 == 3) {
            line_dsc->color  = lv_palette_main(LV_PALETTE_GREEN);
        }
        else {
            line_dsc->color  = lv_palette_lighten(LV_PALETTE_GREY, 1);
        }
    }
}

#endif

Show cursor on the clicked point

#include "../../lv_examples.h"
#if LV_USE_CHART && LV_BUILD_EXAMPLES

static lv_obj_t * chart;
static lv_chart_series_t * ser;
static lv_chart_cursor_t * cursor;

static void value_changed_event_cb(lv_event_t * e)
{
    static int32_t last_id = -1;
    lv_obj_t * obj = lv_event_get_target(e);

    last_id = lv_chart_get_pressed_point(obj);
    if(last_id != LV_CHART_POINT_NONE) {
        lv_chart_set_cursor_point(obj, cursor, NULL, last_id);
    }
}

/**
 * Show cursor on the clicked point
 */
void lv_example_chart_6(void)
{
    chart = lv_chart_create(lv_screen_active());
    lv_obj_set_size(chart, 200, 150);
    lv_obj_align(chart, LV_ALIGN_CENTER, 0, -10);

    //    lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 10, 5, 6, 5, true, 40);
    //    lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_X, 10, 5, 10, 1, true, 30);

    lv_obj_add_event_cb(chart, value_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
    lv_obj_refresh_ext_draw_size(chart);

    cursor = lv_chart_add_cursor(chart, lv_palette_main(LV_PALETTE_BLUE), LV_DIR_LEFT | LV_DIR_BOTTOM);

    ser = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
    uint32_t i;
    for(i = 0; i < 10; i++) {
        lv_chart_set_next_value(chart, ser, lv_rand(10, 90));
    }

    //    lv_chart_set_scale_x(chart, 500);

    lv_obj_t * label = lv_label_create(lv_screen_active());
    lv_label_set_text(label, "Click on a point");
    lv_obj_align_to(label, chart, LV_ALIGN_OUT_TOP_MID, 0, -5);
}

#endif

Scatter chart

#include "../../lv_examples.h"
#if LV_USE_CHART && LV_BUILD_EXAMPLES

static void draw_event_cb(lv_event_t * e)
{
    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_INDICATOR) {
        lv_obj_t * obj = lv_event_get_target(e);
        lv_chart_series_t * ser = lv_chart_get_series_next(obj, NULL);
        lv_draw_rect_dsc_t * rect_draw_dsc = lv_draw_task_get_draw_dsc(draw_task);
        uint32_t cnt = lv_chart_get_point_count(obj);

        /*Make older value more transparent*/
        rect_draw_dsc->bg_opa = (LV_OPA_COVER * base_dsc->id2) / (cnt - 1);

        /*Make smaller values blue, higher values red*/
        int32_t * x_array = lv_chart_get_x_array(obj, ser);
        int32_t * y_array = lv_chart_get_y_array(obj, ser);
        /*dsc->id is the tells drawing order, but we need the ID of the point being drawn.*/
        uint32_t start_point = lv_chart_get_x_start_point(obj, ser);
        uint32_t p_act = (start_point + base_dsc->id2) % cnt; /*Consider start point to get the index of the array*/
        lv_opa_t x_opa = (x_array[p_act] * LV_OPA_50) / 200;
        lv_opa_t y_opa = (y_array[p_act] * LV_OPA_50) / 1000;

        rect_draw_dsc->bg_color = lv_color_mix(lv_palette_main(LV_PALETTE_RED),
                                               lv_palette_main(LV_PALETTE_BLUE),
                                               x_opa + y_opa);
    }
}

static void add_data(lv_timer_t * timer)
{
    lv_obj_t * chart = lv_timer_get_user_data(timer);
    lv_chart_set_next_value2(chart, lv_chart_get_series_next(chart, NULL), lv_rand(0, 200), lv_rand(0, 1000));
}

/**
 * A scatter chart
 */
void lv_example_chart_7(void)
{
    lv_obj_t * chart = lv_chart_create(lv_screen_active());
    lv_obj_set_size(chart, 200, 150);
    lv_obj_align(chart, LV_ALIGN_CENTER, 0, 0);
    lv_obj_add_event_cb(chart, draw_event_cb, LV_EVENT_DRAW_TASK_ADDED, NULL);
    lv_obj_add_flag(chart, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS);
    lv_obj_set_style_line_width(chart, 0, LV_PART_ITEMS);   /*Remove the lines*/

    lv_chart_set_type(chart, LV_CHART_TYPE_SCATTER);

    lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_X, 0, 200);
    lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, 0, 1000);

    lv_chart_set_point_count(chart, 50);

    lv_chart_series_t * ser = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
    uint32_t i;
    for(i = 0; i < 50; i++) {
        lv_chart_set_next_value2(chart, ser, lv_rand(0, 200), lv_rand(0, 1000));
    }

    lv_timer_create(add_data, 100, chart);
}

#endif

Circular line chart with gap

#include "../../lv_examples.h"
#if LV_USE_CHART && LV_DRAW_SW_COMPLEX && LV_BUILD_EXAMPLES

static void add_data(lv_timer_t * t)
{
    lv_obj_t * chart = lv_timer_get_user_data(t);
    lv_chart_series_t * ser = lv_chart_get_series_next(chart, NULL);

    lv_chart_set_next_value(chart, ser, lv_rand(10, 90));

    uint16_t p = lv_chart_get_point_count(chart);
    uint16_t s = lv_chart_get_x_start_point(chart, ser);
    int32_t * a = lv_chart_get_y_array(chart, ser);

    a[(s + 1) % p] = LV_CHART_POINT_NONE;
    a[(s + 2) % p] = LV_CHART_POINT_NONE;
    a[(s + 2) % p] = LV_CHART_POINT_NONE;

    lv_chart_refresh(chart);
}

/**
 * Circular line chart with gap
 */
void lv_example_chart_8(void)
{
    /*Create a stacked_area_chart.obj*/
    lv_obj_t * chart = lv_chart_create(lv_screen_active());
    lv_chart_set_update_mode(chart, LV_CHART_UPDATE_MODE_CIRCULAR);
    lv_obj_set_style_size(chart, 0, 0, LV_PART_INDICATOR);
    lv_obj_set_size(chart, 280, 150);
    lv_obj_center(chart);

    lv_chart_set_point_count(chart, 80);
    lv_chart_series_t * ser = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
    /*Prefill with data*/
    uint32_t i;
    for(i = 0; i < 80; i++) {
        lv_chart_set_next_value(chart, ser, lv_rand(10, 90));
    }

    lv_timer_create(add_data, 300, chart);

}

#endif

API

lv_api_map_v9_1.h

lv_chart.h

lv_types.h