jinglemansweep / lcdproc

Python OOP Wrapper Library for LCDproc Telnet API
http://pypi.python.org/pypi/lcdproc
BSD 3-Clause "New" or "Revised" License
23 stars 18 forks source link

Possible to update all widgets simultaneously? #9

Closed player500 closed 9 years ago

player500 commented 9 years ago

Thanks for making this useful wrapper. I'm implementing a spectrum analyzer with 16 vbar widgets and all of them update individually after length adjustments in a loop, resulting in a very slow refresh. Would be very useful to have the capability to update them manually all at once. Thanks!

ssokolow commented 9 years ago

The first thing to do is confirm that the hardware is fast enough. What LCD device are you using?

For example, while not a lcdproc-compatible device, the ColdTears LCDSysInfo unit is a good example for demonstrating the issue because it does non-monospace font rendering on a bult-in microcontroller and it's flat-out impossible to do atomic screen updates or to update faster than about 200ms per API call.

(I'm not sure, but I may also have another LCD somewhere that's old enough for RS232 bitrate issues to limit the update rate.)

player500 commented 9 years ago

The LCd is hd44780 connected to RPi model B. I'm OK with whole screen refresh rates of 200-250ms but what I'm getting instead is that each of the 16 vbar widgets are updated at that rate sequentially, resulting in x16 slower animation...

ssokolow commented 9 years ago

Directly to the RasPi's expansion header, I'm guessing?

If you're willing to send me your code, I have an HD44780 at the heart of my USB-attached drive bay LCD and I could test to see if something in the RasPi or its drivers is a significant contributor to the latency.

player500 commented 9 years ago

Yes, via i2c. Sure, below's the code. Appreciate your help!

#!/usr/bin/env python

from lcdproc.server import Server

def main():

    lcd = Server(debug=False)
    lcd.start_session()

    screen = lcd.add_screen("Screen1")
    screen.set_heartbeat("off")
    screen.set_duration(10)

    screenHeight = lcd.get_server_info()['screen_height']
    screenWidth = lcd.get_server_info()['screen_width']
    cellHeight = lcd.get_server_info()['cell_height']
    cellWidth = lcd.get_server_info()['cell_width']

    totalHeight = screenHeight * cellHeight
    vBarWidgets = []
    for xs in range(1, screenWidth + 1):
        vBarWidgets.append(screen.add_vbar_widget("vBarWidget" + str(xs), x = xs, y = 2, length = totalHeight))

    number = 0        

    try:
        while True:
            for vBarWidget in vBarWidgets:
                vBarWidget.set_length(number % totalHeight)

            number += 1
            #time.sleep(0.1)

    finally:
        lcd.del_screen(screen.ref)

# Run

if __name__ == "__main__":
    main()
emteejay commented 9 years ago

It may be better to write a barGraphWidget that combines the widget_set commands from all the vBarWidget.set_length into a newline separated string to send as a single server request. The lcdproc server polls its' socket in a timed loop at 32Hz so it is impossible to update faster than that.

ssokolow commented 9 years ago

Sorry for not responding. The last couple of days have been a mess and it wasn't helped by my inability to track down the lcdproc module you're using.

("lcdproc" on PyPI seems to have the same API, but it gives me AttributeError: 'Server' object has no attribute 'get_server_info'and I haven't been able to find time to move beyond that.)

emteejay commented 9 years ago

The package on PyPI is out of date see: https://github.com/jinglemansweep/lcdproc

player500 commented 9 years ago

@emteejay Thanks for pointing me in the right direction. A single server request like you suggested did the trick, however the program crashes after a while with socket.error: [Errno 32] Broken pipe

@ssokolow You have to enable i2c modules on RPi + configure LCDd.conf properly and it should work. http://www.stuart-taylor.net/adafruit-i2c-rgb-16x2-lcdkeypad-raspberry-pi/

emteejay commented 9 years ago

Are you reading all the replies? Every command in the request should return a success response.

player500 commented 9 years ago

Yes, I'm getting "success" responses right until the crash:

success

success

success

success

success

Traceback (most recent call last):
  File "main.py", line 41, in <module>
    main()
  File "main.py", line 36, in main
    lcd.del_screen(screen.ref)
  File "/home/pi/projects/LCDaudio/lcdproc/server.py", line 90, in del_screen
    self.request("screen_del %s" % (ref))
  File "/home/pi/projects/LCDaudio/lcdproc/server.py", line 45, in request
    self.tn.write((command_string + "\n").encode())
  File "/usr/lib/python2.7/telnetlib.py", line 280, in write
    self.sock.sendall(buffer)
  File "/usr/lib/python2.7/socket.py", line 224, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 32] Broken pipe

here is the updated code:

#!/usr/bin/env python

from lcdproc.server import Server
import time

def main():

    lcd = Server(debug=False)
    lcd.start_session()

    screen = lcd.add_screen("Screen1")
    screen.set_heartbeat("off")

    screenHeight = lcd.get_server_info()['screen_height']
    screenWidth = lcd.get_server_info()['screen_width']
    cellHeight = lcd.get_server_info()['cell_height']
    cellWidth = lcd.get_server_info()['cell_width']

    totalHeight = screenHeight * cellHeight
    vBarWidgets = []
    for xs in range(1, screenWidth + 1):
        vBarWidgets.append(screen.add_vbar_widget("vBarWidget" + str(xs), x = xs, y = 2, length = totalHeight))

    number = 0

    try:
        while True:
            serverRequest = ""
            for vBarWidget in vBarWidgets:
                serverRequest += "widget_set %s %s %s %s %s\n" % (screen.ref, vBarWidget.ref, vBarWidget.x, vBarWidget.y, number % totalHeight)
            print lcd.request(serverRequest)
            number += 1
            time.sleep(0.1)

    finally:
        lcd.del_screen(screen.ref)

# Run

if __name__ == "__main__":
    main()

Edit: rebooting the Pi seems to have solved the problem... Thanks again for your help

ssokolow commented 9 years ago

@emteejay Ahh. That makes sense. Sorry I couldn't get around to helping before you solved your problem. The last few days have had me out and about far more than usual.

@player500 I'm not on a RasPi. I offered to try it on an hd44780-based USB device that my desktop LCDproc instance is configured to use in order to rule out the RasPi's i2c as a significant source of delay.