esphome / issues

Issue Tracker for ESPHome
https://esphome.io/
290 stars 35 forks source link

max7219digit unacceptably slow with more than 10 chips/digits #5506

Open goatchurchprime opened 7 months ago

goatchurchprime commented 7 months ago

The problem

The scroll on a 20 character display (num_chips=20) is very slow and ripples. This is much much worse than in my Micropython implementation. The error log shows that the component is blocking:

[14:06:49][W][component:215]: Components should block for at most 20-30ms.
[14:06:50][W][component:214]: Component display took a long time for an operation (0.32 s).

If I chained two of these displays together the update time would be over half a second.

Which version of ESPHome has the issue?

2023.12.9

What type of installation are you using?

pip

Which version of Home Assistant has the issue?

2023.12.9

What platform are you using?

ESP8266

Board

modwifi

Component causing the issue

max7219digit

Example YAML snippet

font:
  - file: "fonts/pixelmixttf"
    id: fontpixmix
    size: 8

spi:
  clk_pin: 4
  mosi_pin: 5

globals:
  - id: displaycount
    type: int
    restore_value: no
    initial_value: '0'

display:
  - platform: max7219digit
    cs_pin: 16
    data_rate: 10MHz   # default 1Mz
    num_chips: 20      # very slow (0.4s) for more than 20 chips, don't know why  
    intensity: 4
    id: idmax7219
    reverse_enable: true
    lambda: |-
      id(displaycount) += 1;
      id(countsincemessage) += 1;
      //it.printf(0, 0, id(fontdragon), ":%d", id(displaycount)); 
      //return;
      it.line(158, 1, 150, 6);
      it.printf(120, 0, id(fontdragon), "#%d", id(displaycount)); 
      it.intensity(max(2, 9 - (int)(id(countsincemessage)/2)));
      if (id(mqttmsg).has_state()) {
        it.print(0, max(0, 8-id(countsincemessage)), id(fontpixmix), id(mqttmsg).state.c_str()); 
      } else { 
        it.strftime(0, 0, id(fontpixmix), "Time:  %H.%M.%S", id(sntptime).now());
      }

Anything in the logs that might be useful for us?

as above

Additional information

It looks like the inefficiency is due to MAX7219Component::display() looping through all the chips/characters and individually calling MAX7219Component::send64pixels() with a huge number of MAX7219_REGISTER_NOOP padding bytes on either side to account for the shift register nature of this device. This N^2 algorithm starts to make a big difference when N>10.

For reference (in case I have time to look at this) my highly optimized (ie obfuscated) Micropython code is below. It looks like it takes advantage of how the blocks of four characters act as shift registers for the next block of four. (I can also set the brightness on each 64 pixel cell individually.)

The current implementation has a lot of multi-line and rotate90/flip_y settings that would make implementing this faster display function a challenge, but it probably could be done by breaking out the inner loop of the send64pixels() into its own getbyte(row,col) function and embedding it in some version of the shift-register one.

def setbrightness(brightness, i0=0, i1=9999):
    for i in range(max(i0, 0), min(i1, numledchars)):
        brightnesschars[(numledchars - i)*2 - 1] = brightness

def showbrightness():
    cs.value(0)  
    spi.write(brightnesschars)
    cs.value(1)

def scrollbrightness(brightness=0):
    for i in range(numledchars - 1):
        brightnesschars[(numledchars - i)*2 - 1] = brightnesschars[(numledchars - (i+1))*2 - 1]
    brightnesschars[1] = brightness

s = bytearray(8)
def show():
    for y in range(8):
        s[6] = s[4] = s[2] = s[0] = y+1
        yp = y*(numledchars) + numledchars
        cs.value(0)  # cannot put in loop as resets the shifting
        for m in range(0, numledchars-1, 4):
            s[1] = buffer[yp - m - 1]
            s[3] = buffer[yp - m - 2]
            s[5] = buffer[yp - m - 3]
            s[7] = buffer[yp - m - 4]
            spi.write(s)
        cs.value(1)

# This resolves to the same setup calls used in esphome function
def init():
    for i in range(0, 10, 2):
        cs.value(0)
        x = b"\x0c\x00\x0f\x00\x0b\x07\x09\x00\x0c\x01"[i:i+2]
        spi.write(x*numledchars)
        cs.value(1)
    showbrightness()
github-actions[bot] commented 3 months ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.