yozik04 / nextion

Nextion serial client
GNU Lesser General Public License v3.0
25 stars 10 forks source link

[Question] Read response from Nextion #1

Closed Chronocento closed 4 years ago

Chronocento commented 4 years ago

Hi, thank you for this library, it's nice to have a lightweight one to push data to Nextion screens. I would like to know how it would possible to read the response back from a screen, like a touch event, or a custom string pushed back in certain occasions. What is the function to read? Thanks!

yozik04 commented 4 years ago

Hi, thank you. Sure it is possible. All callbacks from the display call event_handler that you pass to the Nextion constructor.

def event_handler(type_, data):
    if type_ == EventType.STARTUP:
        print('We have booted up!')

    logging.info('Event %s data: %s' % type, str(data))

async def run():
    client = Nextion('/dev/ttyS1', 9600, event_handler)

In Nextion editor you need to tick "Send Component ID" for the event. Then display will send payload back.

Chronocento commented 4 years ago

Thank you for the fast reply! Indeed, in my project I don't use the "Send Component ID" for when an item is clicked, because I'm interacting with the interface through the Expansion Board, and when I use the "click" command, even if "Send Component ID" is ticked, it isn't sent back; so, I use "prints", sending some simple integers back.

Now, I tried again with a simple test interface (with the touchscreen enabled) and two buttons: one sends its component ID, the other one uses "prints". The first one works fine with your code, but I had to change logging.info('Event %s data: %s' % type, str(data)) to logging.info('Data: ' + str(data)) otherwise it would throw this error:

TypeError: not enough arguments for format string ^CTraceback (most recent call last): File "nextion_test.py", line 41, in <module> loop.run_forever() File "/usr/lib/python3.5/asyncio/base_events.py", line 421, in run_forever self._run_once() File "/usr/lib/python3.5/asyncio/base_events.py", line 1388, in _run_once event_list = self._selector.select(timeout) File "/usr/lib/python3.5/selectors.py", line 445, in select fd_event_list = self._epoll.poll(timeout, max_ev)

With the second button, it seems that prints doesn't send anything back; but pressing the first button, what I receive is the concatenation of the various prints (for this example, I tried 1) and the first button component ID (here, it has been pressed three times):

2020-01-27 15:42:49,115 - DEBUG - Handle event: b'e\x00\x01\x00' 2020-01-27 15:42:49,116 - INFO - Data: Touch(page_id=0, component_id=1, touch_event=0) 2020-01-27 15:42:49,116 - DEBUG - received: b'' 2020-01-27 15:43:21,182 - DEBUG - received: b'0100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000000100000065000100' 2020-01-27 15:43:21,183 - DEBUG - received: b'' 2020-01-27 15:43:26,243 - DEBUG - received: b'65000100' 2020-01-27 15:43:26,244 - DEBUG - Handle event: b'e\x00\x01\x00' 2020-01-27 15:43:26,245 - INFO - Data: Touch(page_id=0, component_id=1, touch_event=0) 2020-01-27 15:43:26,246 - DEBUG - received: b'' 2020-01-27 15:43:28,077 - DEBUG - received: b'65000100' 2020-01-27 15:43:28,077 - DEBUG - Handle event: b'e\x00\x01\x00' 2020-01-27 15:43:28,078 - INFO - Data: Touch(page_id=0, component_id=1, touch_event=0) 2020-01-27 15:43:28,079 - DEBUG - received: b'' 2020-01-27 15:43:31,412 - DEBUG - received: b'0100000065000100' 2020-01-27 15:43:31,413 - DEBUG - received: b''

I gave a look at your code and, if I'm not mistaken, you are using the Nextion Return Data format to understand what you are receiving back from the screen. I don't see anything related to something sent from the screen that is not linked to a touch event.

Do you think that it will be possible to somehow add another kind of event to the ones that the library is expecting to parse from the serial, so that prints works? Or maybe is possible to "emulate" the touch event through prints (adding the same hex header, before the actual value)?

Let me know if/how I can help!

Chronocento commented 4 years ago

Ok, I think that I managed to "solve" the issue by using the printh command: I "simulate" an actual interface element by sending the same hexadecimals that Nextion Return Data uses for touch screen events: printh 65 00 01 01 FF FF FF From the NRD reference:

0x65 0x00 0x01 0x01 0xFF 0xFF 0xFF Returned when Touch occurs and component’s corresponding Send Component ID is checked in the users HMI design. 0x00 is page number, 0x01 is component ID, 0x01 is event (0x01 Press and 0x00 Release) data: Page 0, Component 1, Pressed

Now I have to understand how to parse the data: str(data) returns Touch(page_id=0, component_id=1, touch_event=1) Is there a better way than to use various splits? Like an array whose data is accessible through indexes?

yozik04 commented 4 years ago

The first one works fine with your code, but I had to change logging.info('Event %s data: %s' % type, str(data)) to logging.info('Data: ' + str(data))

Actually it should be logging.info('Event %s data: %s', type, str(data)). There was a typo.

yozik04 commented 4 years ago

I gave a look at your code and, if I'm not mistaken, you are using the Nextion Return Data format to understand what you are receiving back from the screen. I don't see anything related to something sent from the screen that is not linked to a touch event.

You are right, yes.

Do you think that it will be possible to somehow add another kind of event to the ones that the library is expecting to parse from the serial, so that prints works?

This is a way to go. Do you have a vision how it should be implemented? In theory we can receive and handle any payload from the display. Even textual.

yozik04 commented 4 years ago

Ok, I think that I managed to "solve" the issue by using the printh command: I "simulate" an actual interface element by sending the same hexadecimals that Nextion Return Data uses for touch screen events: printh 65 00 01 01 FF FF FF

That is an alternative way.

You can access data via simple dot.

def event_handler(type_, data):
    if type_ == EventType.TOUCH:
        print(data.page_id)
        print(data.component_id)
        print(data.touch_event)
dezfutak commented 3 years ago

Doing something like:

thisbutton=data,component_id

from within the event_handler function

...is fine, but I want to then monitor such button touches from the async def run() function; what's a way to do that?