Event function with extra argument

Description

I’m in the classic situation where i set a callback function to a button with:

lv_obj_set_event_cb(btn_tmp, ssid_selected);

and the function:

void ssid_selected(lv_obj_t * obj, lv_event_t event){
  /* My code */
}

But now i whant to insert an extra argument to the function, such as:

lv_obj_set_event_cb(btn_tmp, ssid_selected(string));

and

void ssid_selected(lv_obj_t * obj, lv_event_t event, char *string){
  /* My code */
}

How can i do this?

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

ESP32 on Arduino Toolchain.

What do you want to achieve?

An action function with an extra argument to my button.

What have you tried so far?

The code above mentioned.

Hi,
See:


and:

1 Like

or:

1 Like

Thanks, i will give a try.

Ok, i’m using lv_obj_set_user_data and lv_obj_get_user_data. But now the problem is that the object is a local variable, so i can’t call it in the callback function. I show you why.
I need to create a button list of available wifi networks around me, so i used the following commands:

      WiFi.mode(WIFI_STA);
      WiFi.disconnect();
      delay_ms(10);

      int n = WiFi.scanNetworks();

      if (n == 0) {

      } else{
        String ssid[n];
        String rssi[n];
        for (int i = 0; i < n; ++i) {
          ssid[i] = WiFi.SSID(i);
          rssi[i] = WiFi.RSSI(i);
          delay_ms(10);
        }

        wifi_lst = lv_list_create(lv_scr_act(), NULL);
        lv_obj_set_size(wifi_lst, 300, 180);
        lv_obj_align(wifi_lst, NULL, LV_ALIGN_CENTER, 0, 0);

        uint8_t ssid_buf_len = 80;
        uint8_t str_buf_len = 100; 
        for(int i = 0; i<n; i++) {
          char ssid_char[ssid_buf_len];
          char str_char[str_buf_len];
          (ssid[i] + "  (" + rssi[i] + " dB)").toCharArray(str_char, str_buf_len);
          lv_obj_t *btn_tmp = lv_list_add_btn(wifi_lst, LV_SYMBOL_WIFI, str_char);
          lv_obj_set_user_data(btn_tmp,ssid_char);
          lv_obj_set_event_cb(btn_tmp, ssid_selected);
        }
      }

But, obviously, the callback function can’t call the object btn_tmp. How can i do this?

The object that’s passed to the event callback is btn_tmp.

You are right. But how can i use lv_obj_get_user_data? I’m trying something like:

void ssid_selected(lv_obj_t *obj, lv_event_t event){
  if(event == LV_EVENT_SHORT_CLICKED){
    Serial.println((char*)lv_obj_get_user_data(obj));
    lv_btn_set_state(wifi_btn, LV_BTN_STATE_TGL_REL);
    lv_obj_del(wifi_lst);

    wifi_icon_update();
  }
}

But it returns nosense strings.

ssid_char is a local variable, so it loses its value outside of that for loop. You’ll need to allocate a buffer to store that string in and then call lv_obj_set_user_data with the pointer to that buffer.

Perfect, it works! Thank you,
Alberto.

One moment. I noticed it return always the last button! How can i solve it?
I wonder what is the sense of using lv_obj_get_user_data with a global variable, if i can take that variable directly.

If you have a global variable, you can use that variable directly. In this case, the value of ssid_char is specific to each button, so you would want to use user_data for that.

What returns the last button? lv_list_add_btn returns a pointer to whatever button it created.

I mean the following.
I have three networks around me. When i create the list with

    for(int i = 0; i<n; i++) {
      (ssid[i] + "  (" + rssi[i] + " dB)").toCharArray(str_char, str_buf_len);
      ssid[i].toCharArray(ssid_char,ssid_buf_len);
      lv_obj_t *btn_tmp = lv_list_add_btn(wifi_lst, LV_SYMBOL_WIFI, str_char);
      lv_obj_set_user_data(btn_tmp,ssid_char);
      lv_obj_set_event_cb(btn_tmp, ssid_selected);
    }

I can see all three networks ordered by signal quality (we call them WiFi1, WiFi2 and WiFi3).
I used global variables, as you suggested:

// Variables for wifi networks list
const uint8_t ssid_buf_len = 80;
const uint8_t str_buf_len = 100;
char ssid_char[ssid_buf_len];
char str_char[str_buf_len];

My callback function is

void ssid_selected(lv_obj_t *obj, lv_event_t event){
  upper_bar_update();
  if(event == LV_EVENT_SHORT_CLICKED){
    Serial.println((char*)lv_obj_get_user_data(obj));
    lv_btn_set_state(wifi_btn, LV_BTN_STATE_TGL_REL);
    lv_obj_del(wifi_lst);
  }
}

But whichever connection I choose it prints always the last one.

I didn’t suggest using a global variable. I suggested allocating a buffer (e.g. using malloc or lv_mem_alloc) for each string. When you use a global variable it will also get overriden each time the for loop runs.

I never used it. How can i implement malloc in my code? When can i use free to deallocate memory?

You can free the memory when the button is deleted (LV_EVENT_DELETE should work for this purpose).

if(event == LV_EVENT_DELETE){
    free(lv_obj_get_user_data(obj));
}

Perfect, it works!