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.
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()
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.
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:
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
Anything in the logs that might be useful for us?
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 owngetbyte(row,col)
function and embedding it in some version of the shift-register one.