Script control the CS pin to write multiple displays with a single display instance

Hi, I am working to make the Button+ in ESPHome. The Button+ knows multiple configurations with in the max 7 displays, 6 of them are the same type.

My problem is that is if I define 6 displays, I run in to ram problems the device does not have PSRAM so this is not an option… So I discussed this problem with the owner and he suggested to update the displays 1 by 1 by cycling through the CS pins. Like this:

  - id: cs_pin_bar_left
    platform: gpio
    pin:
      mcp23xxx: base_J3
      number: 5
      mode:
        output: true
      inverted: true
  - id: cs_pin_bar_right
    platform: gpio
    pin:
      mcp23xxx: base_J3
      number: 1
      mode:
        output: true
      inverted: false

display:
  - id: bar_display
    platform: ili9xxx
    model: ST7735
    color_order: bgr
    update_interval: never
    dc_pin: 
      number: GPIO37
      allow_other_uses: true
    invert_colors: false
    show_test_card: false
    dimensions: 
      height: 160
      width: 80
      offset_width: 24

script:
  - id: update_bar1_right
    then:
      - lambda: |-
          id(cs_pin_bar_left).turn_on();
          id(cs_pin_bar_right).turn_off();
          id(bar_display).fill(id(orange));
          id(bar_display).update();
          delay(10);
          id(cs_pin_bar_right).turn_on();

For some reason if I execute the update_bar1_script the display stays black. I asumed the we have a timing issue, so I am trying to use the set_cs_pin() method form the ili9xxx display component something like this below:
I am not an C++ expert, but by searching the web, this is where I landed ;). It is giving me a hard time…

  - id: update_bar_left
    then:
      - lambda: |-
          auto pin = esphome::mcp23xxx_base::MCP23XXXGPIOPin();
          pin.set_parent(base_J3);
          pin.pin_mode(esphome::gpio::Flags::FLAG_OUTPUT);
          pin.setup();
          esphome::GPIOPin* newpin = pin;
          id(bar_display).set_cs_pin(newpin);
          id(bar_display).fill(id(orange));
          id(bar_display).update();

But I have the feeling that the GPIOPin class seems not to be meant to be used on this level…

Any suggestions?
Thanks in advanced

I have no idea how to answer your question but thank you for showing us this cool device.

That won’t help as each display instance will still allocate a screen-sized buffer, and doesn’t release it just because the CS pin is disabled.

What is the resolution of the small displays? You can use LVGL to drive multiple displays, each instance can work with a buffer as small as 1/4 the screen size. That might work.

@clydebarrow thank you for sharing your insight!
The idea is to release the CS pin when the data is writen. (display.update)
The following cycle is what I have in mind:

  • on boot write all cs pins high
  • write cs pin from display 1 low
  • draw the display 1 with a script (lines/text/icons)
  • display_id.update()
  • write cs pin from display 1 high
  • write cs pin from display 2 low
  • draw the display 2 with a script (lines/text/icons)
  • display_id.update()
  • write cs pin from display 2 high
  • etc.

The thing is if i create 1+6 separate instances it will crash on ram shortage. (software resets when i execute a display.update()) I am not sure how LVGL can help me with this…

P.S. the resolution is:

    dimensions: 
      height: 160
      width: 80

on Tweakers there is a hole topic on running ESPHome on a Button+:
https://gathering.tweakers.net/forum/list_messages/2270086

Might be useful :slight_smile:

1 Like

@septillion check the last post there :wink:
DiXY == MartijnD :sunglasses:

1 Like

Oh, gotcha now - one display instance, 6 CS pins separately controlled. Best way would be to not specify the CS pin in the display config, and use update_interval: never. The from a script turn on one CS pin, set a global variable to identify the display to update, then call the display update method.

However, you are still going to have to do something clever to get the init sequence sent to all displays. That’s going to be harder. Oh maybe send it to all displays simultaneously.

Using LVGL instead would mean 6 ili9xxx instances, but no display lambdas, and a 25% buffer in LVGL. That would mean for 6 displays you only need 1.5 times the display buffer size (the display component does not allocate a buffer in this case.)

The main display could also use a small buffer, so overall RAM usage will be down. And you would not have to jump through hoops to control the displays.

1 Like

I have 2 orange displays now here! I could trigger the init sequence by running:
id(bar_display).setup(); ESPHome: /opt/build/esphome/esphome/components/ili9xxx/ili9xxx_display.cpp Source File

Now I need to figure out if I need if i need to do this every update or will a on_boot do the trick.

Thanks for the kick in the right direction! I will do some more testing and will also give the LVGL library a try it also takes care of some other things (like paging).