LVGL chart performace impact

Hi everyone!

I’m trying to get LVGL charts to work, sadly ESPHome does not have those integrated yet.

So what I’m doing is:

interval:
  - interval: 30sec
    then:
       lambda: |-
           draw_chart();

and in my test “chart.h” to draw random stuff between -2 and 6

static void draw_chart()
{

    lv_obj_t * chart;
    chart = lv_chart_create(lv_scr_act());

    lv_obj_set_size(chart, 480, 150);
    lv_obj_set_pos(chart, 0, 170);
    lv_obj_set_style_bg_color(chart, lv_color_make(50, 50, 50), LV_PART_MAIN);
    lv_obj_set_style_size(chart, 0, LV_PART_INDICATOR);

    lv_chart_set_div_line_count(chart, 0, 0);
    lv_chart_set_type(chart, LV_CHART_TYPE_LINE);
    lv_chart_set_point_count(chart, 60);
    lv_chart_set_update_mode(chart, LV_CHART_UPDATE_MODE_SHIFT);
    lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_X, 0, 60);
    lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, -2, 6);

    lv_chart_series_t * ser1;
    ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_BLUE), LV_CHART_AXIS_PRIMARY_Y);

    uint32_t i;
        for(i = 0; i < 60; i++) {
        lv_chart_set_next_value(chart, ser1, (int32_t)lv_rand(-2, 6));
    }
}

and it works suprisingly well :smile:


(The button and switch is done through ESPHome’s LVGL)

The problem:
The longer I run this, the slower my ESP32 gets until it crashes and restarts.

See the loop time:

Any ideas what the problem is?

Possibly a memory leak, caused by the fact you are recreating the chart widget every time you call the function? Not that I have any solution, my workaround to the lack of charts was to use online_image: with the Puppet add-on and Apexcharts…

Create the chart once, then update it on an interval.

Any plans to add charts to ESPHome’s LVGL Clyde?

Yes, discussing that in discord

4 Likes

No idea how I would do that, but good to know that the integration is coming!

Okay - it works!
Not sure how elegant it is :grimacing:

esphome:
[...]
    build_flags:
       - -D LV_USE_CHART=1
  includes:
    - chart.h
  on_boot:
    then:
      - delay: 5sec
      - lambda: |-
            draw_chart();

interval:
  - interval: 100ms
    startup_delay: 10sec
    then:
      - lambda: |-
            update_chart();

Main problem was that I have no idea how C++ works and I guess I needed to put those static thingies outside the functions.

chart.h:

static lv_obj_t * chart;
static lv_chart_series_t * ser;

static void draw_chart()
{
          chart = lv_chart_create(lv_scr_act());

          lv_obj_set_size(chart, 480, 150);
          lv_obj_set_pos(chart, 0, 170);
          lv_obj_set_style_bg_color(chart, lv_color_make(0, 0, 0), LV_PART_MAIN);                 //Chart background color
          lv_obj_set_style_size(chart, 0, LV_PART_INDICATOR);                                     //Points on line size
          lv_obj_set_style_line_color(chart, lv_color_make(50, 50, 50), LV_STATE_DEFAULT);        //Horizontal div lines color
          lv_obj_set_style_border_color(chart, lv_color_make(50, 50, 50), LV_STATE_DEFAULT);      //Border color

          lv_chart_set_div_line_count(chart, 8, 0);
          lv_chart_set_type(chart, LV_CHART_TYPE_LINE);
          lv_chart_set_point_count(chart, 60);
          lv_chart_set_update_mode(chart, LV_CHART_UPDATE_MODE_SHIFT);
          lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_X, 0, 60);
          lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, -2, 6);

          ser = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_BLUE), LV_CHART_AXIS_PRIMARY_Y);
}          

static void update_chart()
{
          uint32_t i;
              for(i = 0; i < 60; i++) {
              lv_chart_set_next_value(chart, ser, (int32_t)lv_rand(-2, 6));
          }
}

Now off to learn even more C++ and figure out how to get actual values from a HomeAssistant sensor in there :face_with_spiral_eyes:

If you just want to start plotting from the time the ESP is booted up, the simplest way is to use a single platform: homeassistant sensor and store the values in a std::vector, then iterate through that vector to plot.

I have some example code if you get stuck.

2 Likes

Thanks, platform: homeassistant is what I needed!

Many examples said to use arrays or vectors, but I didn’t really get the point.
For now I’m doing:

  - platform: homeassistant
    name: "Battery Amps"
    id: BattAmps
    entity_id: sensor.effective_bat_cur
static void update_chart()
{
    lv_chart_set_next_value(chart, ser, (id(BattAmps).state));
}

It’s plotting from right to left with whatever interval I set on the update - so exactly what I needed :+1:

1 Like

Any examples are important to learn how it works.
I want to make a graphical interface on ESPHOME LVGL (because I like this platform, its capabilities and the relative simplicity of creating code, compared to the Arduino IDE) that will display something like an oscillogram of signal changes from a sensor in real time.
In fact, i am talking about the ad8232 heart monitor sensor and I want to make a portable cardiograph with a graphical interface on the touch screen. Of course, this is not a professional medical device, but only a household tool for basic diagnostics and a reason to call a real doctor for consultations. Similar devices have already been created on Arduino and ESP, but the esphome environment allows many other useful features.
Next idea is to create a multimeter with signals analyser.
There is a GRAPH component, I have not yet tried how it will work with LVGL, but @clydebarrow announced good news.

Storing the values isn’t strictly neccesary for charts in LVGL, but handy if you want to say zoom on a particular portion then redraw what you had. I use it for drawing on a canvas.

This is some example code that adds a sensor to a vector then logs the contents of that vector.

globals:
  # Define a global variable as a vector with type float.
  - id: chart_values
    type: std::vector<float>

sensor:
  - platform: homeassistant
    entity_id: sensor.man_cave_temperature
    force_update: True
    id: man_cave_temperature
    # If your sensor doesn't have a fixed update interval, use a filter to force it to have one.
    filters:
      - throttle_average: 60s
    on_value:
      lambda: |-
        // Store sensor value (x) at the END of the vector
        id(chart_values).push_back(x);
        // Limit the number of values in the vector to 60, delete the FIRST one (oldest) if we have 61
        if (id(chart_values).size() == 61) {
          id(chart_values).erase(id(chart_values).begin()); 
          }
        // Now print the contents of the vector out, oldest to newest
        int counter = 0;
        // Use an iterator to access the values.  Iterators are like pointers for vectors but contain the value they point to
        std::vector<float>::iterator it;
        for (it = id(chart_values).begin(); it != id(chart_values).end(); ++it) {
          ++counter;
          ESP_LOGD("Value", "Temp is %f, count is %i", *it, counter);
        }

I would love to see a complete example of the chart drawn from a vector including the C code if possible?