Reporting X/Y from a Nextion screen

Hello

I have been trying to report the position of the touched point on a Nextion screen.

Got buttons, text, sliders, variables, images etc all working fine, but the reporting part I can’t seem to crack, and I think it maybe because I am not formatting the command correctly.

I am not using the clumsy Nextion library (have not needed to), and Perry has given some excellent pointers in his stickied thread (wasn’t sure whether to post in there… thought better here).

The Nextion command guide states: ( https://nextion.tech/instruction-set/ )

Command:
*tch0,tch1,tch2,tch3 *
Touch
Coordinates x.val=tch0, y.val=tch1
Readonly. When Pressed tch0 is x coordinate, tch1 is y coordinate.
When released (not currently pressed), tch0 and tch1 will be 0.
tch2 holds the last x coordinate, tch3 holds the last y coordinate.

Returned (Length=9)

0x67 Touch Coordinate (awake) 0x67 0x00 0x7A 0x00 0x1E 0x01 0xFF 0xFF 0xFF
Returned when sendxy=1 and not in sleep mode
0x00 0x7A is x coordinate in big endian order,
0x00 0x1E is y coordinate in big endian order,
0x01 is event (0x01 Press and 0x00 Release)
(0x00256+0x71,0x00256+0x1E)
data: (122,30) Pressed

My short ‘test’ routine (and its incorrect I know)… is here. It throws back very random numbers and not what I was expecting.
Page 7 by the way is the page within my Nextion programming that gives me a blank page to use.

#include <Nextion.h>                                                                    // Library for the Nextion screen

#define NEXTION_PORT Serial1

Nextion nex(NEXTION_PORT);

Long SwipeX;

//---------------------------------------

void setup() {
  
  Serial.begin(9600);                                                                   // For serial debug
  Serial1.begin(9600);                                                                  // Serial to Nextion

  Serial1.print("rest");Serial1.print("\xFF\xFF\xFF");                                  // Reset the Nextion screen

  delay(100);

  Serial1.print("dim=100");Serial1.print("\xFF\xFF\xFF");                               // Set brightness to 100%

  Serial1.print("page ");Serial1.print(7);Serial1.print("\xFF\xFF\xFF");;               // Change to swipe page
  Serial1.print("sendxy=1");Serial1.print("\xFF\xFF\xFF");                              // Turn on co-ord reporting

//---------------------------------------
}

void loop() {
  
     Serial1.print("tch0");Serial1.print("\xFF\xFF\xFF");                               // Request touched X co-ord

    
     if (Serial1.available()) {
        SwipeX= Serial1.read();if (SwipeX!=255){Serial.println(SwipeX);}
     }

}

I have literally thrown all kinds of variations of that command at it. Tried reading the result as 9 individual Serial.reads etc (It reports 255 all the time if nothing comes back - hence the !=255).
It’s odd that I get random numbers when I touch the screen, but there seems to be no pattern and certainly not 9 variables in a row as I am expecting (am I even correct there?).

It says the result is in big endian order, which I don’t understand. but, I still would expect a result I could deduce something from. This is totally random and returns 2 or 3 odd variables at best.

I never see 103 (0x67), which I thought would be the starting value.

I worked out the “sendxy=1” to start the XY reporting. Nothing came back at all until I sent that.

Ideas?

This reports 4 values. It’s messy I know, but it is a debug hacked loop to try and get this working.
The startup request for the brightness and screen reset doesn’t seem to play well with this for some reason (fine in my main code). So commented that out, as it’s not actually needed on a fresh boot.

I don’t quite understand the expected returned values (which are currently uselessly random).
In the Nextion command list, it says that you send “tch0” to request the x value.

This should return:
0x67 0x00 0x7A 0x00 0x1E 0x01 0xFF 0xFF 0xFF. First byte is the header, last 3 are the 255’s.
The 0x00, 0x7A, 0x00 and 0x1E are the returned Big endian values (deal with that calculation later).

But, the same bit of Nextion instruction says that 0x00 and 0x7A is the x value, and 0x00 and 0x1E is the y value.
So why bother requesting the y value using “tch1”? - if you get them anyway when you request “tch0”.

#include <Nextion.h>                                                                    // Library for the Nextion screen

#define NEXTION_PORT Serial1

Nextion nex(NEXTION_PORT);

byte X1;
byte X2;
byte X3;
byte X4;
byte Chk1;
byte Chk2;
byte Chk3;

long Xvalue;


//---------------------------------------

void setup() {
  
  Serial.begin(9600);                                                                   // For serial debug
  Serial1.begin(9600);                                                                  // Serial to Nextion

  //Serial1.print("rest");Serial1.print("\xFF\xFF\xFF");                                  // Reset the Nextion screen
  delay(100);
  //Serial1.print("dim=100");Serial1.print("\xFF\xFF\xFF");                               // Set brightness to 100%
  delay(100);
  Serial1.print("page ");Serial1.print(7);Serial1.print("\xFF\xFF\xFF");;               // Change to swipe page
  delay(100);
  Serial1.print("sendxy=1");Serial1.print("\xFF\xFF\xFF");                              // Turn on co-ord reporting


  
//---------------------------------------
}

void loop() {
     
     Serial1.print( "tch0");Serial1.print("\xFF\xFF\xFF");                              // Request touched X co-ord

         if (Serial1.read()==0x67){                                                     // Verification header of 103 (0x67)
                  X1=Serial1.read();                                                    // Returned position value
                  X2=Serial1.read();
                  X3=Serial1.read();
                  X4=Serial1.read();
                  Chk1=Serial1.read();                                                  // Chk values should all be 255
                  Chk2=Serial1.read();
                  Chk3=Serial1.read();
      
                  if ((Chk1==255) and (Chk2==255) and (Chk3==255)){
                       Serial.println("Valid X value returned");
                       Serial.println(X1);
                       Serial.println(X2);
                       Serial.println(X3);
                       Serial.println(X4);
                       
                       Serial.println("-----------------------");
                       Serial.println("Waiting for next touch...");
                  }
                  
         }

}

Hmm

Hello Sparky,

I am not using the clumsy Nextion library (have not needed to), and Perry has given some excellent pointers in his stickied thread (wasn't sure whether to post in there... thought better here).

Thank you for posting a separate question, this is about your project not my tutorial; a separate question is appropriate.

You say you are using my methods (thank you :slight_smile: ), so why have you got the Nextion.h library included in your code?

I don't know the answer to your question and judging by the comments and questions I have read on these fora in the time I have been here I suspect no one else does either. However, here are some thoughts:

Consider why you want this at all, I cannot imagine (but my imagination is limited ! ! !) a project that would need the Nextion to report touch co-ordinates to the controller.

I've not tried to understand every last bit of your sample code but this:

void loop() {
 
     Serial1.print("tch0");Serial1.print("\xFF\xFF\xFF");                               // Request touched X co-ord

   
     if (Serial1.available()) {
        SwipeX= Serial1.read();if (SwipeX!=255){Serial.println(SwipeX);}
     }

}

Isn't going to work very well. You send the request to the Nextion for the data, then check the serial port once then send the request again, then check the serial port once, then.... At best the Nextion will be confused! I'm not surprised you are getting all sorts of nonsense back. I suspect you are forgetting that the serial port, especially at 9600 baud, is way slower then the Arduino can run code. You are not waiting for the request to be sent or for the reply to be received before checking for a single byte then sending the request again.

Have a look at part 4 of the tutorial and look at the code in the 'additional features' file. I have added code to send Nextion return codes to the serial monitor; it waits until it has received a complete string from the Nextion, ending 0xff 0xff 0xff, then send it to the monitor. Take that code and modify to your needs. You need to arrange it that the requests are not sent so often, but not using delay(); as that will mess up receiving data, use millis(); some how.

The other problem you have is you are trying to read tch0 and tch1, which are only valid while the screen is being touched. I suggest you read tch2 and tch3, at least for experimenting, as they remain valid after the screen has been touched, so timing the read with the screen being touched is not so important.

Please update this with your results so it helps others.

Thanks and good luck.

++Karma; // Because I like to reward anyone who is making a proper effort to work out how to do stuff.

Thanks Perry.

The Nextion commands are left over from my playing around with the GET statement - they can go.

I tried that code you posted earlier. It's random at best.

Yes, I realised I was bombarding the screen with commands.
I did have a delay, but used 'delay' which is clearly wrong. I will attend to that.

The reason for the x/y co-ords, is I want the ability to swipe left, right, up or down to achieve an action within the confines of the screen.
So, the actual result doesn't need to be pinpoint (I won't be anyway), but it needs a starting value, and then whether that value goes up or down + direction

I tried that code you posted earlier. It's random at best.

Yes, I realised I was bombarding the screen with commands.

I suspect those 2 things are linked....

The reason for the x/y co-ords, is I want the ability to swipe left, right, up or down to achieve an action within the confines of the screen.

OK, you are in new territory for me, never tried that. Do you want the swipes to be anywhere on the screen or in specific places? If in specific places use a slider.

It needs to be anywhere on the screen.
I thought of sliders, but they are not that responsive and you can’t achieve both axis at once

Sparky2019:
It needs to be anywhere on the screen.
I thought of sliders, but they are not that responsive and you can't achieve both axis at once

OK.

I suggest you use my 'additional features' code and add a new function that uses millis(); to send the code for reading tch2 once per second and tch3 once per the half a second in between and watch the return data in the serial monitor, then decide what to do with it once you are happy with it.

This produces 'a result'. But it's anything but consistent or makes any sense.

The expected first variable Header (I assume they are bytes)... should be the 0x67 (103).

But in reality, that 103 appears randomly within the printed results, if at all.

I can't really await a returned serial stream of data, as it spews constant 255's all the time. Maybe I should use that as the test for returned valid data.

Shame it doesn't only return the data upon a touch.

tch0 or tch2 doesn't really help at this point.

I still don't understand whether the 4x returned variables X1-X4 are all to do with X, or whether X1&X2 are X and X3&X4 are Y (as the Nextion instruction set says).
If it returns both axis from the single tch0 or tch2 command, you would not need tch1 or tch3.

byte Header;
byte X1;
byte X2;
byte X3;
byte X4;
byte Event;
byte Chk1;
byte Chk2;
byte Chk3;

long Xvalue;

unsigned long CurrentMillis;
unsigned long RetrieveMillis;


//---------------------------------------

void setup() {
  
  Serial.begin(9600);                                                                   // For serial debug
  Serial1.begin(9600);                                                                  // Serial to Nextion

  Serial1.print("page ");Serial1.print(7);Serial1.print("\xFF\xFF\xFF");;               // Change to swipe page

  Serial1.print("sendxy=1");Serial1.print("\xFF\xFF\xFF");                              // Turn on co-ord reporting

  RetrieveMillis=millis();
  
//---------------------------------------
}

void loop() {
  
     CurrentMillis = millis();

     if (CurrentMillis - RetrieveMillis > 3000){
          RetrieveMillis = CurrentMillis; 

           Serial.println( "Requesting...");      
     
           Serial1.print( "tch2");Serial1.print("\xFF\xFF\xFF");                          // Request X co-ord
           delay(100);

           Header=Serial1.read();Serial.println(Header);                                  // Should be 0x67
           X1=Serial1.read();Serial.println(X1);
           X2=Serial1.read();Serial.println(X2);
           X3=Serial1.read();Serial.println(X3);
           X4=Serial1.read();Serial.println(X4);
           Event=Serial1.read();Serial.println(Event);                                    // Touch or release
           Chk1=Serial1.read();Serial.println(Chk1);
           Chk2=Serial1.read();Serial.println(Chk2);
           Chk3=Serial1.read();Serial.println(Chk3);
           Serial.println("-----------------------");

                    
              
     }
  
}

OK, I have something that works.... .rightly or wrongly.

First observation. The Serial.reads seem to need a delay(10) otherwise they fail 50% of the time.

The ReadScreen routine is what I use to read my button presses. The button id number is always the 3rd reported value and that seems to work well.

So, I added a button over the entire screen that is then used to 'ask' for the screen co-ords.
You have to turn off co-ord reporting immediately after getting the position, otherwise it screws up all further comms (button presses etc).

This here is reporting the X/Y position pretty reliably upon a touch.

byte ScreenData;
byte ScreenDump;

byte Header;
byte X1;
byte X2;
byte Y1;
byte Y2;
byte Event;
byte Chk1;
byte Chk2;
byte Chk3;

long Xvalue;
long Yvalue;

//---------------------------------------

void setup() {
  
  Serial.begin(9600);                                                                      // For serial debug
  Serial1.begin(9600);                                                                     // Serial to Nextion

  Serial1.print("page ");Serial1.print(7);Serial1.print("\xFF\xFF\xFF");;                  // Change to swipe page
  delay(100);
  Serial1.print("sendxy=0");Serial1.print("\xFF\xFF\xFF");                                 // Turn off co-ord reporting

  
//---------------------------------------
}

void loop() {

     if (Serial1.available() > 0) {ReadScreen();}

     if (ScreenData==3){                                                                    // The screen has been touched using button b3, so request co-ords
  
           Serial.println("Screen touched");      

           Serial1.print("sendxy=1");Serial1.print("\xFF\xFF\xFF");                         // Turn on co-ord reporting

           delay(300);
     
           Serial1.print("tch0");Serial1.print("\xFF\xFF\xFF");                             // Request X co-ord
           delay(10);

           
           Header=Serial1.read();//Serial.println(Header);                                  // Should be 0x67 (103)
           if (Header!=255){
               delay(10); 
               X1=Serial1.read();//Serial.println(X1);                                      // X co-ord
               delay(10);
               X2=Serial1.read();//Serial.println(X2);
               delay(10);
               Y1=Serial1.read();//Serial.println(Y1);                                      // Y co-ord
               delay(10);
               Y2=Serial1.read();//Serial.println(Y2);
               delay(10);
               Event=Serial1.read();//Serial.println(Event);                                // Touch or release
               delay(10);
               Chk1=Serial1.read();//Serial.println(Chk1);    
               delay(10);
               Chk2=Serial1.read();//Serial.println(Chk2);
               delay(10);
               Chk3=Serial1.read();//Serial.println(Chk3);
               Serial.println("-----------------------");

               Xvalue=X2+(X1*256);Serial.print("X position= ");Serial.println(Xvalue);
               Yvalue=Y2+(Y1*256);Serial.print("Y position= ");Serial.println(Yvalue);
           }
   
           Serial1.print("sendxy=0");Serial1.print("\xFF\xFF\xFF");                         // Turn off co-ord reporting
            
     }
}


//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void ReadScreen(){


    ScreenDump = Serial1.read();//Serial.println(ScreenDump);
    delay(5);
    ScreenDump = Serial1.read();//Serial.println(ScreenDump);
    delay(5);
    ScreenData = Serial1.read();//Serial.println(ScreenData);
    delay(5);
    ScreenDump = Serial1.read();//Serial.println(ScreenDump);
    delay(5);
    ScreenDump = Serial1.read();//Serial.println(ScreenDump);
    delay(5);
    ScreenDump = Serial1.read();//Serial.println(ScreenDump);
    delay(5);
    ScreenDump = Serial1.read();//Serial.println(ScreenDump);
}

Still don't understand the point of tch1 and tch3 if tch0 and tch2 report Y position anyway :confused:

In fact, I get the same result from all 4 different tch commands.

Next problem to address is that this only retrieves a valid X/Y from a brief touch and once you have lifted off the screen again. Until you let go, it reports -1.

I need a co-ord upon touching, and a co-ord when I let go.

But, its progress....

Well i don't seem to be able to get it to report a value with anything less than a brief touch.
Longer than a brief touch always retrieves invalid data.

Maybe I will have to go with the less sophisticated option of a grid of touch buttons over the screen and try and read those to determine the swipe.
Not sure the buttons are that sensitive however.

This reliably retrieves the X/Y. But, only if you tap the screen.

Anything longer (holding the screen) corrupts the data and it fails.

Nothing I try seems to cure that.

//Nextion X/Y retrieval

byte ScreenData;
byte ScreenDump;

byte Header;
byte X1;
byte X2;
byte Y1;
byte Y2;
byte Event;
byte Chk1;
byte Chk2;
byte Chk3;

long Xvalue;
long Yvalue;

//---------------------------------------

void setup() {
  
  Serial.begin(9600);                                                                      // For serial debug
  Serial1.begin(9600);                                                                     // Serial to Nextion

  Serial1.print("page ");Serial1.print(7);Serial1.print("\xFF\xFF\xFF");                   // Change to swipe page
  delay(100);
  Serial1.print("sendxy=0");Serial1.print("\xFF\xFF\xFF");                                 // Turn off co-ord reporting

  
//---------------------------------------
}

void loop() {

     if (Serial1.available() > 0) {ReadScreen();}

     if (ScreenData==3){                                                                    // The screen has been touched using button b3, so request co-ords
  
           Serial.println("Screen touched");  

           Serial1.print("sendxy=1");Serial1.print("\xFF\xFF\xFF");                         // Turn on co-ord reporting

           delay(100);
     
           Serial1.print("tch0");Serial1.print("\xFF\xFF\xFF");                             // Request X co-ord
           
           delay(10);
           
           Serial1.print("sendxy=0");Serial1.print("\xFF\xFF\xFF");                         // Turn off co-ord reporting
           
           delay(10);
       
           Header=Serial1.read();//Serial.println(Header);                                  // Should be 0x67 (103)
           if (Header==103){
               delay(10); 
               X1=Serial1.read();//Serial.println(X1);                                      // X co-ord
               delay(10);
               X2=Serial1.read();//Serial.println(X2);
               delay(10);
               Y1=Serial1.read();//Serial.println(Y1);                                      // Y co-ord
               delay(10);
               Y2=Serial1.read();//Serial.println(Y2);
               delay(10);
               Event=Serial1.read();//Serial.println(Event);                                // Touch or release
               delay(10);
               Chk1=Serial1.read();//Serial.println(Chk1);    
               delay(10);
               Chk2=Serial1.read();//Serial.println(Chk2);
               delay(10);
               Chk3=Serial1.read();//Serial.println(Chk3);

               if ((X1==255) and (X2==255) and (Y1==255) and (Y2==255)){}                   // Probably returned due to invalid data

               else {            
                      Xvalue=X2+(X1*256);Serial.print("X position= ");Serial.println(Xvalue);    //Valid co-ords
                      Yvalue=Y2+(Y1*256);Serial.print("Y position= ");Serial.println(Yvalue);
               }

               Serial.println("-----------------------");
               
           }
           
           do{Serial1.read();} while (!Serial1.available());                                // Ensure the buffer is clear

     }
}

//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void ReadScreen(){                                                                          // Get value of screen operation


    ScreenDump = Serial1.read();//Serial.println(ScreenDump);
    delay(5);
    ScreenDump = Serial1.read();//Serial.println(ScreenDump);
    delay(5);
    ScreenData = Serial1.read();//Serial.println(ScreenData);
    delay(5);
    ScreenDump = Serial1.read();//Serial.println(ScreenDump);
    delay(5);
    ScreenDump = Serial1.read();//Serial.println(ScreenDump);
    delay(5);
    ScreenDump = Serial1.read();//Serial.println(ScreenDump);
    delay(5);
    ScreenDump = Serial1.read();//Serial.println(ScreenDump);
}

So I tried the entire screen covered with 50 individual (non overlapping) buttons.
Fail. The touchscreen just isn't fast enough.

So, I think the Nextion is not up to this task unless I can somehow cure the data corruption issue when you do anything than tap the screen.

But I am out of ideas on that one.

Right Sparky,

There’s too much wrong with your code for me to go into everything. The main thing is you MUST STOP using delay to wait for things. Delay is a blunt instrument that stops everything else from working and generally messes up any chance of your code working. Learn to use millis, there are tutorials on here about millis, find them and understand them. Start with blink without delay. Also study my code and Using millis for timing

The correct Nextion instruction for reading a value is:

get

So, the 4 instructions you need are

get tch0
get tch1
get tch2
get tch3

tch 0 and tch 1 hold the current X and Y values of where the screen is being touched while it is being touched, tch 2 and tch 3 hold the last X and Y values when the screen is no longer being touched.

I’ve modified my ‘Nextion additional features’ code to request tch2 and tch3 (you can easily change for 0 and 1) every 2 seconds (you can easily change the time interval) and send the result to the serial monitor, I have attached the modified code to this reply along with the HMI file you will need to make it work. In the modified code look at loop();, there is a function call to request_touch_data();, and look in the tab _02_functions, there is the function request_touch_data();. Look how that works and modify to your needs.

You also need to study the section of the function HMI_read(); that saves the return codes, and the function print_return_codes();, which sends them to the serial monitor. Note that it prints the last 10 bytes, it DOES NOT just print the last return code, so it’s a bit hard to read until you get used to it. Maybe I’ll improve that in a later version but for now that’s how it works.

Let me know how you get on, but if I see any more delays in your main code I’ll not be happy :o !

Enjoy.

2020-01-18 Nextion_additional_features.zip (26.2 KB)

Thanks Perry

Didn't realise that delays were so horrific. Only just got used to not using Strings ::slight_smile:

I already have millis() set up for other timing requests in my main code, so I know how to implement them.

Its just that delay is so much shorter and quicker. Oh well.

I will have a look later tonight. Many thanks for your time