Unable to change button style

Description

Hi! I have a problem with style change. this is how i do it.

  1. Create a window
  2. Call function that adds buttons, button action function changes state of relay depending on which button was pressed.
  3. Constatntly check if the state of relay changed ( relay state can be changed not just manualy but also automaticly).
  4. If relay state changes call lv_btn_set_style to change button color. in this step program crashes and for the love of god I cannot figure out why. What I am sure of it’s that in the moment when lv_btn_set_style is called CPU restarts.

Thank you very much for your help and feel fre to ask any additional questions.

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

ESP32, Arduino

What do you want to achieve?

Change of button style.

What have you tried so far?

If style is changed in “Manual_Ctrl” function the program does not crash

Code to reproduce

// Manual control window
void OpenManWin_Event (lv_obj_t * btn, lv_event_t event) {

  if (event == LV_EVENT_RELEASED) {
    /*Create a window*/
    ManualWin = lv_win_create(lv_scr_act(), NULL);
    lv_win_set_title(ManualWin, "  Manual control");
    lv_win_set_style(ManualWin, LV_WIN_STYLE_HEADER, &lv_style_plain_color);
    lv_win_set_style(ManualWin, LV_WIN_STYLE_CONTENT, &lv_style_transp_tight);
    lv_win_set_btn_size(ManualWin, 40);
  
    /*Add control buttons to the header*/
    // Close window button
    lv_obj_t * close_btn = lv_win_add_btn(ManualWin, LV_SYMBOL_CLOSE);           
    lv_obj_set_event_cb(close_btn, lv_win_close_event_cb);
    // Set Regime button
    lv_obj_t * Regime_bnt = lv_win_add_btn(ManualWin, LV_SYMBOL_PLAY);           
    lv_obj_set_event_cb(Regime_bnt, Regime_Ctrl);

    // Create container
    container = lv_cont_create(ManualWin, NULL);
    lv_cont_set_fit(container, LV_FIT_FLOOD);
    lv_obj_set_style(container, &lv_style_transp);
    lv_cont_set_layout(container, LV_LAYOUT_GRID);

    // Create Output modules
    Add_Manual_Ctrl_Btn (container, OutputModule[KOMPRESSOR], KOMPRESSOR_TR, 0);
    Add_Manual_Ctrl_Btn (container, OutputModule[FAN], FAN_TR, 1);
    Add_Manual_Ctrl_Btn (container, OutputModule[STIRRER], STIRRER_TR, 2);
    Add_Manual_Ctrl_Btn (container, OutputModule[PUMP], PUMP_TR, 3);
    Add_Manual_Ctrl_Btn (container, OutputModule[VALVE1], VALVE1_TR, 4);
    Add_Manual_Ctrl_Btn (container, OutputModule[VALVE2], VALVE2_TR, 5);
    Add_Manual_Ctrl_Btn (container, OutputModule[VALVE3], VALVE3_TR, 6);
    Add_Manual_Ctrl_Btn (container, OutputModule[SPARE], SPARE_TR, 7);
  }
}

// Create Manual control buttons
void Add_Manual_Ctrl_Btn (lv_obj_t *parent, lv_obj_t * btn, char *Btn_txt, uint32_t Btn_id) {

  btn = lv_btn_create(parent, NULL);    
  lv_obj_set_size(btn, 150, 40);
  lv_obj_set_user_data(btn, Btn_id);
  lv_obj_set_event_cb(btn, Manual_Ctrl);         
  
  lv_obj_t * Signal_label;
  Signal_label = lv_label_create(btn, NULL);
  lv_btn_set_style(Signal_label, LV_LABEL_STYLE_MAIN, &lv_style_plain);
  lv_label_set_text(Signal_label, Btn_txt);
  lv_obj_align(Signal_label, NULL, LV_ALIGN_CENTER, 0, 0);

  lv_btn_set_style(btn, LV_BTN_STYLE_REL, &Style_Plane);
  lv_btn_set_style(btn, LV_BTN_STYLE_PR, &Style_Plane);
}

// Manual control function
void Manual_Ctrl (lv_obj_t * btn, lv_event_t event) {

  if (event == LV_EVENT_RELEASED) {
    uint32_t id = lv_obj_get_user_data(btn);
    
    switch (id) {
      case 0: db.Relays.Compressor.HMICmd = true;
        break;
      case 1: db.Relays.Fan.HMICmd = true;
        break;
      case 2: db.Relays.Stirrer.HMICmd = true;
        break;
      case 3: db.Relays.Pump.HMICmd = true;
        break;
      case 4: db.Relays.Valve1.HMICmd = true;
        break;  
      case 5: db.Relays.Valve2.HMICmd = true;
        break;  
      case 6: db.Relays.Valve3.HMICmd = true;
        break;  
      case 7: db.Relays.Spare.HMICmd = true;
        break;  
    }
  }
}

// Detect Relay Status change in Manual function window
void DetectRelayStatusChange(void) {

  if (db.Relays.Spare.HMIStatus != RelayStatus_Old[SPARE]) {
    RelayStatus_Old[SPARE] = db.Relays.Spare.HMIStatus;

    RefreshRelayStatus(OutputModule[SPARE], db.Relays.Spare);
  }
}

// Refresh Relay Status in Manual function window
void RefreshRelayStatus(lv_obj_t * btn, relay_t &Relay) {
  if (Relay.XS) {
    lv_btn_set_style(btn, LV_BTN_STYLE_REL, &Style_Green);
    lv_btn_set_style(btn, LV_BTN_STYLE_PR, &Style_Green);
  }
  else {
    lv_btn_set_style(btn, LV_BTN_STYLE_REL, &Style_Plane);
    lv_btn_set_style(btn, LV_BTN_STYLE_PR, &Style_Plane);
  }
}

Where is DetectRelayStatusChange called? Is it from an interrupt handler? You can’t call any LittlevGL functions besides lv_tick_inc in an interrupt handler.

It’s called in the void loop() every cycle just after lv_task_handler(), is this a problem?

No; that should be fine. I don’t really see any problem with this code. :confused:

Under this section What have you tried so far?, I should mention:
I also tried to change the style in function “Manual_Ctrl” where it worked ok. Then I also tried to create a task which is executed every 500ms, in this task function I call “RefreshRelayStatus” again program crashes.

Do you maybe have any other approach how to handle change of style on outside trigger?

When you call RefreshRelayStatus are you sure that the btn value being passed to it is correct?

You are on to something, I moved style change for testing purpose in function “Manual_Ctrl” which is called on button event, as seen below

// Manual control function
void Manual_Ctrl (lv_obj_t * btn, lv_event_t event) {

  if (event == LV_EVENT_RELEASED) {

    uint32_t id = lv_obj_get_user_data(btn);
    
    switch (id) {
      case 0: db.Relays.Compressor.HMICmd = true;
        break;
      case 1: db.Relays.Fan.HMICmd = true;
        break;
      case 2: db.Relays.Stirrer.HMICmd = true;
        break;
      case 3: db.Relays.Pump.HMICmd = true;
        break;
      case 4: db.Relays.Valve1.HMICmd = true;
        break;  
      case 5: db.Relays.Valve2.HMICmd = true;
        break;  
      case 6: db.Relays.Valve3.HMICmd = true;
        break;  
      case 7: db.Relays.Spare.HMICmd = true;
          if (db.Relays.Spare.XS) {
            lv_btn_set_style(OutputModule[SPARE], LV_BTN_STYLE_REL, &Style_Green);
            lv_btn_set_style(OutputModule[SPARE], LV_BTN_STYLE_PR, &Style_Green);
          }
          else {
            lv_btn_set_style(OutputModule[SPARE], LV_BTN_STYLE_REL, &Style_Plane);
            lv_btn_set_style(OutputModule[SPARE], LV_BTN_STYLE_PR, &Style_Plane);
          }
        break;
    }
  }
}

In this situation program crashes but if I use btn instead of OutputModule[SPARE] it’s OK.
OutputModule[SPARE] is of a type lv_obj_t * OutputModule[8];
What could be the problem here, any ideas?

The most likely problem is that whatever value is in OutputModule[SPARE] is invalid (i.e. not pointing to a button). This could mean that it wasn’t initialized (properly) or that memory corruption is occuring.

If you are using LittlevGL 6.1+, try enabling the debug features in lv_conf.h; the assertions should help catch invalid pointers.