mathoudebine / turing-smart-screen-python

Unofficial Python system monitor and library for small IPS USB-C displays like Turing Smart Screen or XuanFang
GNU General Public License v3.0
1.13k stars 188 forks source link

Putting text to specific location #1

Closed goooroooX closed 3 years ago

goooroooX commented 3 years ago

Hi!

I'm wondering, if you can provide some information on how to put the text on the screen to specific position (as native application do)? We can always print on the picture directly and upload it, but there is no screen buffer and picture update is really slow via COM interface, so visually it will look just ugly.

Thank you in advance.

mathoudebine commented 3 years ago

Hi, thanks for your interest in this project !

You are right, the picture update is really slow and not convenient for real-time data display. When this project was created I just ported the bare minimum of the original software, but there is indeed some functions to display text without full refresh available from the screen comm. protocol.

I cannot provide more information because there is no official protocol documentation, but i have some C# code that implements this feature that needs to be ported to Python.

I will try to make this available soon ! If you want to implement this yourself, you can reach me in private and i will send you the C# code.

goooroooX commented 3 years ago

Hi, Unfortunately I have no any significant experience with C#, I'm using Python mostly. This would be really great if you will find some time to port this feature to Python. Thank you!

dinvlad commented 3 years ago

Same here, this would be really helpful! Also, do you mean that a full-screen update is slow, but can it still be used for "indicator display" updates in pseudo real-time? Or more specifically:

  1. Numeric values?
  2. Percentage bars?
  3. Graph displays?

FWIW, if it's "good enough" for updating indicators, we could then use https://github.com/adalisan/rainmeter-python to integrate with a much nicer UI.

EDIT: I've just ordered the display - will poke around this myself as well once it arrives.. From what I understood, you figured out all commands by monitoring serial communication from the vendor app, right?

mathoudebine commented 3 years ago

From what I've seen when using the vendor app, a specific command can be used to update part of the screen only (like text, numeric values, percentage and graphs as well). For this, the text has to be converted to picture first.

This "partial update" command is indeed much faster than the full update, and is quite like real-time (not instant though, especially if you have a lot of content to update) I'm currently implementing this command in the Python code of this repository. The integration of rainmeter-python is a good idea!

To find the commands, i've used JetBrains dotPeek to decompile the vendor app and load the source code in Visual Studio. I've had much success with the chinese version named 35inchNEW.rar

mathoudebine commented 3 years ago

I've created the feature/text-display branch with the new feature to display text (including partial refresh of the LCD). I've added some examples for the text, and a timer that refresh as fast as possible: you will see that the partial update is indeed very fast when updating only small portion of the screen Feel free to launch the code from this branch and tell me what you think! Is the rendering good on your screen?

dinvlad commented 3 years ago

Awesome, will test it once I receive mine next week! :smile: Sorry for prematurely bugging you with all these questions - it'll become obvious once I try.

As an off-topic, I've also ordered a regular 3.5" HDMI screen to compare. I really like how slim and easy-to-connect the Turing device is though. The normal screen has bigger dimensions due to the bulky ports, and needs quite a bit more work to fit everything nicely - custom HDMI ribbon and micro-USB cables with angled connectors, a 3D-printed case, and sanding/de-soldering the Pi header somehow..

If Turing works great, I may poke more into the rainmeter-python idea then. I want to get to the point where the only feature missing from Turing is custom animations (like some avatar/spinner rotation animation, although we could get creative there with pixel art as well - it can be cute in its own way..).

One other OT question, what do you use to design your own backgrounds?

goooroooX commented 3 years ago

Running on Windows throws exception:

  File "main.py", line XXX, in <module>
    signal.signal(signal.SIGQUIT, sighandler)
AttributeError: 'module' object has no attribute 'SIGQUIT'

Fixed with:

is_linux = os.name == 'posix'
if is_linux:
    signal.signal(signal.SIGQUIT, sighandler)

Other than that - it works like a charm! Few charts will make me completely happy, but it's already pretty decent in current state.

20210910_023059

dinvlad commented 3 years ago

Btw, looks like your new code already addresses another question I had: https://github.com/mathoudebine/turing-smart-screen-python/blob/8116fb7262c91c5c07742ff5eac398c58bd68742/main.py#L78-L80

I'm assuming DISPLAY_WIDTH*4 is the absolute max number of bytes we could send at once? If not, increasing it further up to the limit should obviously speed things up even more, since lesser time would be spent on the overhead of serial communication (mostly between Python -> serial library -> non-realtime OS serial interface, where each syscall could add an extra several dozen random msec).

Thinking further about the limits, looks like the absolute 16-bit pixel rate here is 115,200 baud rate / (2 symbols * 10 bits/symbol) = 5760 pixels/sec, i.e. ~4% of the screen estate per sec. Not too bad but not sure how far we can stretch it :-) Have you played with higher baud rates at all? Asking since I had successes in my past Arduino-based projects, where I could up it significantly; some say it could even go all the way to 3Mbaud.. Maybe it would even be possible to see what kind of serial bus they use on the chip, and adjust it accordingly.

mathoudebine commented 3 years ago

Fixed with:

is_linux = os.name == 'posix'
if is_linux:
    signal.signal(signal.SIGQUIT, sighandler)

Thanks, added the fix in the pull request.

Btw, looks like your new code already addresses another question I had:

https://github.com/mathoudebine/turing-smart-screen-python/blob/8116fb7262c91c5c07742ff5eac398c58bd68742/main.py#L78-L80

I'm assuming DISPLAY_WIDTH*4 is the absolute max number of bytes we could send at once? If not, increasing it further up to the limit should obviously speed things up even more, since lesser time would be spent on the overhead of serial communication (mostly between Python -> serial library -> non-realtime OS serial interface, where each syscall could add an extra several dozen random msec).

You're right: by increasing the number of bytes sent from 320 to 320*4 in this PR, the screen full refresh time has been divided by two (from ~5s to ~3s). It seems now that the LCD hardware is the limit, because even by sending more bytes at once, the refresh time doesn't decrease. I will try with higher baud rates.

As an off-topic, I've also ordered a regular 3.5" HDMI screen to compare. I really like how slim and easy-to-connect the Turing device is though. The normal screen has bigger dimensions due to the bulky ports, and needs quite a bit more work to fit everything nicely - custom HDMI ribbon and micro-USB cables with angled connectors, a 3D-printed case, and sanding/de-soldering the Pi header somehow..

I agree with you, i chose the Turing screen because it is already packaged and needs only one USB-C connection for both video and power. The downside is that the screen is not recognized as a standard monitor by operating systems.

One other OT question, what do you use to design your own backgrounds?

I'm currently using a background that come from the vendor app. I will try to include more backgrounds in the resources

As for the charts & animations, it has to be made manually by generating images and sending them to the display, so it may be included in another PR later but not planned for now!

dinvlad commented 3 years ago

the screen full refresh time has been divided by two (from ~5s to ~3s).

Wow, that's really surprising! It should've taken ~25 sec at the least, based on the theoretical serial throughput from above :thinking: So something else must be going on..

dinvlad commented 3 years ago

OK, I finally received the screen. I'm seeing some very strange results:

  1. The initial background picture upload is indeed only ~3 sec, which seems fast
  2. Upload speed doesn't seem to depend on baud rate: I varied it between 1 and 10,000,000, and that didn't make a difference (maybe the device forces its own preset rate?)
  3. The dynamic timestamp display seems rather slow: once I increase the font size to 50, it becomes visibly laggy (you can see "band of progress" for each individual numeral updating).

All of this seems kinda puzzling to me. Not sure I'll actually keep it, after seeing the performance in (3)..

mathoudebine commented 3 years ago

OK, I finally received the screen. I'm seeing some very strange results:

  1. The initial background picture upload is indeed only ~3 sec, which seems fast

  2. Upload speed doesn't seem to depend on baud rate: I varied it between 1 and 10,000,000, and that didn't make a difference (maybe the device forces its own preset rate?)

Indeed the baudrate might be forced by the device.

  1. The dynamic timestamp display seems rather slow: once I increase the font size to 50, it becomes visibly laggy (you can see "band of progress" for each individual numeral updating).

All of this seems kinda puzzling to me. Not sure I'll actually keep it, after seeing the performance in (3)..

Performance is the real problem with this device. Not sure if it can be improved in the code, maybe I'm missing something... I shall try the Windows software to see what is the rendering.

@goooroooX I have implemented the support of progress bar in the latest commit of the main branch, feel free to check it out ! I have also added the font from the vendor app (which is the GeForce official font) and some backgrounds with examples also extracted from the vendor app themes (check the res/themes folder). My favourite is the Cyberpunk one!

goooroooX commented 3 years ago

Defining types within functions has broken Python 2.x support. Yeah, I know that 2.x is ancient and EOL, but I have to use it sometimes. Progress bar does not work for me as-is, it flickers once per 10 seconds or so. Fixed with:

bar_filled_width = int(float(value) / (max_value - min_value) * width)