Tutorial on how to write code with Nextion and Arduino

Hello everyone,
Although I am a new signed-in user, I have been watching and learning from the community of arduino.cc .
I started to study on HTML5, CSS, JavaScript, Java, Arduino and robotics about one year ago. Since then, the help from this site was very useful to achive many things. This is my first post and I, in turn, want to give some hours of hard work and knowledge back to the community.

I just finished the tutorial I had been preparing and already uploaded it at my website, https://www.seithan.com .

Here is the Tutorials Web page:

And the link to download the zip file with both the .HMI file for Nextion and .ino file for Arduino, of the Nextion Tutorial
https://seithan.com/download-nt-ino/?

If you prefer to see more colors, you can go to my site, but I will also upload it here. If you want, you can see a short video on youtube on how the project works. Nextion Tutorial: How to write code with Nextion and Arduino - YouTube

There are many comments on both the .HMI file and .ino file, to explain better what we are doing.

Now, we are beginning to write the tutorial:

Tutorial on how to write code with Nextion and Arduino

In this tutorial, one can find simple tips and hints on how to control a Nextion display with an Arduino. There are examples on:
• How to initialize Nextion’s Serial baud rate
• How to send commands from Nextion and assign them to functions on Arduino
• How to organize the commands from Nextion and read them through the Serial
• How to send commands from Arduino to Nextion
• How to change components’ attributes at Nextion from Arduino
• How to use < Serial.print > commands
• How to use < sprintf > command
• How to make a useful pop-up message on Nextion
How to use the following on Nextion with simple examples on where and how they can be applied:
• Read the current page id using Nextion < dp > command and a useful example on where it can be applied
• How to make a variable, change it to global scope and use it across different pages
• How to use Array[index] for p[] and b[]
• How to use < vis > command to make components visible or invisible
• How to use the timer variable, a non-visible component
• How to use the < xstr > command for printing text using defined area for text rendering (see t1 textbox Press Event on page with ID=1)
• How to print a moving text on Nextion with < xstr >
• How to use the < covx > command to convert numeric to text or the opposite (see examples at the b0 and b1 Events on page with ID=2)
• How to use the return character < “\r” > on text boxes for new line
• How to add a numeric variable inside a text (same with arduino)
• How to revert the minval and maxval of a slider
• How to make a slider horizontal or vertical
To present all the above in action, we made a single project in which we included all of them.
As a scenario for the project, we choose to read from a text array, stored in the Arduino.
We chose that scenario, because we can have many alternative choices and needs, in the code of both Nextion and Arduino without the need of any external hardware, e.g. sensors.
In this way, we can run the project using only an Arduino and the Nextion Editor, from the debug mode.
In the project, the pages might look the same, but the methods are different. We also use pop-up messages to show the code or comments.

Communication protocol between Arduino and Nextion Display:

The communication between Nextion Display and Arduino is quite simple. There is no need for libraries and complicate commands as Nextion uses a simple and complete instruction set. A library cannot cover and guess all the needs of the projects wide field.
A custom communication protocol is used because of the following advantages:

  1. We don’t use any Nextion LCD library
  2. Nextion has a protocol to send the ID of different components via Serial, but it is very difficult to use it.
  3. The produced code size is small.
  4. The communication data length is sort. This causes faster communication between Arduino and Nextion LCD.
  5. The protocol can be extended from user by adding his own commands.
  6. It is more practical to send a specific command and then from Arduino’s Serial identify the command that is sent and assign it to the function we want,
    than trying to identify and assign the IDs from a big number of objects to a function, as Nextion and the most libraries do this with the event’s ID protocol.

The only thing that we must do, is to organize the commands that we are going to use with the simplest possible method.
Identifying them with the Arduino code and assigning them to the functions we want.
For this protocol, we want to thank again Vassilis Serasidis for his advises on this.
We use the following Data Format: <#>
An example with the group command “Page” is:

  • From Nextion we send in HEX, by writing: < printh 23 02 50 00 >
  • Means: < # 2 P 0 >
  • <#> declares that a command is following
  • declares the number of bytes that follows (len = 2,

    is the one Byte and <0> is the other)

  • declares the group of commands ( P = page L=Line)
  • declares the ID, to which the command is referring to. (id = 0)
    This means that we are in page 0 (we have written in every page’s "preinitialize event" the command printh 23 02 50 xx (where xx is the page id in HEX, 00 for 0, 01 for 1, etc.).
    We have assigned a function named < first_refresh_page (uint8_t page) > and with a switch case command, we send to each page the data that must updated in first load.
    A second example is this with the “Line” command group (<#> <nextion_var> )
  • From Nextion we send in HEX the following:< printh 23 03 4C 04 01 >
  • Means < # 3 L 4 1 >
  • <#> declares that a command is followed
  • declares the number of bytes that will follow (len = 3, is the one Byte <4> and <1>is the other)
  • declares the group of commands (P = page L=Line)
  • <nextion_var> is the Number of the variable on Nextion that we want to write
  • is the number of the text array Line that we want to store into Nextion's variable
    We have assigned the function < sending_text (byte nextion_var, byte cnt) > where we send the last 2 bytes to local variables of the function

How we read and identifie the commands from Arduino’s Serial port:
CODE SAMPLE FROM .ino FILE

void Nextion_serial_listen() 
{
    if(Nextion.available() > 2){                // Read the Serial. If more than 2 bytes come (we always send more than 2, <#> <len> <cmd> <id>
        char start_char = Nextion.read();      // Then, create a local variable (start_char). Read and store the first byte on it. 
        if(start_char == '#'){                // And when we find the character <#>,
          uint8_t len = Nextion.read();      // We create a local variable (len). Read and store the value of the second byte
                                            // <len> is the length (number of bytes following) 
          unsigned long tmr_1 = millis();
          boolean cmd_found = true;
            
          while(Nextion.available() < len){ // Waiting for all the bytes that we declared with <len> to arrive              
            if((millis() - tmr_1) > 100){    // Waiting... But not forever... 
              cmd_found = false;              // tmr_1 is a timer to avoid getting stuck in the while loop if there are no available bytes on Serial
              break;                           // We MUST NOT leave a while loop running without a timer, because for some reasons that we can not predict,
                                                // the while loop could run forever. Example: a wire of the Serial could have been cut 
            }                                     
            delay(1);                            // Delay for nothing take it off if you think
          }                                   
                                               
            if(cmd_found == true){            // So, A command is found (bytes in Serial buffer equal more than len)
              uint8_t cmd = Nextion.read();  // Create local variable (cmd). Read and store the next byte. This is the command group
                                             
              switch (cmd){
                                    
                case 'P': /*or <case 0x50:> IF 'P' matches, we have the command group "Page". 
                           *The next byte is the page <id> according to our protocol.
                           *It reads the next byte as a type of <uint8_t variable> and direct sends it to:
                           *first_refresh_page() function as parameter. 
                           */
                  first_refresh_page((uint8_t)Nextion.read());  
                  break;
                
                case 'L': 
                /* < case 0x4C: > (0x4c = ‘L’) IF there is a matching with 'L' then we have the command group "Line" according to the protocol
                 * We are waiting 2 more bytes from the <nextion_var> and the <cnt>... (<#> <len> <cmd> <nextion_var> <cnt>)
                 * <nextion_var> is the Number of the variable on Nextion that we want to write
                 * <cnt> is the number of the text array Line that we want to store into Nextion's variable
                 * From Nextion we have sent < printh 23 04 4C 04 xx > where 04, in this example, the <nextion_var> and xx the <cnt>
                 * Same as the above, we are going to read the next 2 bytes as <uint8_t> and direct send them to
                 * < sending_text () > function as parameters to the local variables of the function 
                 * < sending_text(byte nextion_var, byte cnt) > that will be created on startup of the Function
                 */

                    sending_text((uint8_t)Nextion.read(),(uint8_t)Nextion.read()); 
                    
                break;
                
              }
            }
        }  
    }    
}

Now, let’s explain what we do in every page.
Page with id=0, or as we have named it, “home”:
Here we have the first page, which loads when Nextion starts. In here, we set Nextion’s baud rate by typing on the < Preinitialize Event>: “baud=115200”.
We have added a variable with a global scope, that we have named “curr_page_id”, to keep track of the pages’ id, by storing it into the variable. We use it to return to previous page after the pop_up message screen. We also use it to navigate between the pages (Previous / Next, with the precondition that the pages are in order). For the background, I made a picture with my logo. If the screen is pressed anywhere, then the next page will appear.
To do this, we have putted in the Touch Press Event of the page and all of its components this command: < page pop_up >, where pop_up is the name of a page with a welcome message and some information. Press anywhere on the message screen to go to the page with the first example.

Page with id=1, or as we have named it, “first_example”:
In this page, we created five variables, va0, va1, va2, va3, va4, one after the other so their IDs are in order.
We have set the variables type attribute < sta > to STRING from NUMBER and the < txt_maxl > MaxLenth (Maximum Memory Space) to 40 characters.
In variables va0, va1, va2, va3, we store the text that is going to be printed on the four lines of the screen.
Variable va4, is used to store the next line that we “asked” Arduino for, by pressing the up (b0) or down (b1) button.
The va4 is going to replace the va0 or the va3, according to which button is pressed.
These four variables, va0, va1, va2, va3, are the variables that are printed on the screen with this order:

display order when b1 is pressed when b0 is pressed
va0 = first line va0.txt=va1.txt va3.txt=va2.txt
va1 = second line va1.txt=va2.txt va2.txt=va1.txt
va2 = third line va2.txt=va3.txt va1.txt=va0.txt
va3 = fourth line va3.txt=va4.txt va0.txt=va4.txt

Page with id=2, or as we have named it, “second_example”:
In this page, we created 20 variables (va0, va1...va19) one after the other so their IDs would be in order. We use them to store all of the lines of text.
The first thing we created on this page, was those variables, so their IDs would start from 1 and end at 20
We have setted again the variables type attribute < sta > to STRING from NUMBER and the < txt_maxl > MaxLenth (Maximum Memory Space) to 40 characters.
In < va0 > we store the text of the first position of the text array < text[0] >. In < va1 > we store the text of the second position of the text array < text[1] > and so on...
It will end when we reach the ARRAY_ROWS value.
The code will adapt automatically if the lines of the text (ARRAY_ROWS) are less than twenty and is going to use as many lines as the ARRAY_ROWS is.
It will NOT adapt if there are more than twenty lines. In this case, we have to create more variables.
Nextion has more space for variables than Arduino. For that reason, most times it is better to store the text on Nextion and doing all the reading and processing from there, letting Arduino and the Serial available for other tasks of the project. The time to transfer the text array is very little, at the scale of milliseconds.
In addition, Nextion’s MCU has faster clock speed, of 48MHz on Basic, 108MHz on Enhanced over 3.5” and 200MHz on Intelligent, whereas the Arduino’s Nano and UNO have a clock speed of 16MHz. Thus, we should prefer to perform most of the tasks on Nextion rather than Arduino.

We have added a “Hotspot” component, from the toolbox, to store the code with the four < xstr > commands, that print on the display the four lines that we want to.

sys0=22
xstr 15,sys0,390,30,0,BLACK,34784,0,1,1,b[cnt.val].txt
xstr 15,sys0+30,390,30,0,BLACK,34784,0,1,1,b[cnt.val+1].txt
xstr 15,sys0+60,390,30,0,BLUE,34784,0,1,1,b[cnt.val+2].txt
xstr 15,sys0+90,390,30,0,BLUE,34784,0,1,1,b[cnt.val+3].txt

The counter first has the value 1. With the first < xstr >, we are printing the text value of the component with ID = cnt.val = 1. Component va0.
With the second < xstr >, we are printing the text value of the component with ID = (cnt.val + 1) = 2. Component va1.
The third < xstr >, we print the b[cnt.val + 2] = b[3]. Component va2.
And at the end, we print the b[cnt.val+3] = b[4]. Component va3
AS YOU NOTE, we can use the b[.id] component array, which takes component .id as index, instead of < text > or va0.txt or the objname.txt of the < string > argument in < xstr >.
It is also important to NOTE, that in the component array, we can put the variables and equations with numbers and the result of those, will be the number of that specific argument.
e.g.: If cnt.val=1, then b[cnt.val+3] = b[1+3] = b[4].
By pressing the down button(b1), we add to cnt.val, 1(cnt.val+1).
By pressing the up button(b0), we subtract by 1 the cnt.val (cnt.val-1).
With the Hotspot’s Press Event, we print four lines, starting with the one that has the same number as the cnt.val.

A slider has also been added that changes the value of the counter accordingly and by clicking the Hotspot < click n0,1 >, prints the four lines again with the new cnt.val

Page with id=3, or as we have named it, “self_scrl”:
On this page, we chose to send the lines of text one by one, directly to component g1, which is a Scrolling text box on Nextion. We have set maximum amount of characters, < txt_maxl >, to 2000
When this page loads, Arduino identifies it using our custom protocol, which can be found below, and sends the lines of text with a new line character, < \r >, at the end.

void send_text_to_g1()
{
 char temp_data[50];
  
  for(int i = 0; i < ARRAY_ROWS; i++){
    sprintf(temp_data, "g1.txt+=\"%s\\r\"", text[i]);
    Nextion.print(temp_data);
    NextionEndCommand();
  }
}

Component g1 now has all the text stored in it. Display and Scrolling functions are executed through the default operations of Nextion’s Scrolling text.
From g1 attribute menu, we set the < en > attribute to 1, which is going to start the scrolling of the text.
With the use of a timer, we let the g1 component to self scroll for ONLY five seconds from the load of the page, to fill the display with the first five lines of text.
We stop the self scrolling, by writing in the timer’s event:
g1.en=0 //Disable the self scrolling (stop the moving of the text)
tm0.en=0 //Disable the timer, because if we don’t disable it, the timer is going to run the event every five seconds
Now, the scrolling function will start when we hold down the b1 (down) or b0 (up) buttons.
In b1 Press Event, we enable the g1

More details on Page with ID = 1, or as we have named it, “first_example”:
@Each time the page loads, we send a command to Arduino through the preinitialize event of the page, to let the Arduino know on what page we are on.
For this page the command is: < 23 02 50 01 > in hex.
This command uses a specific protocol of our own. Its function on how we organize our commands and make Arduino identify them, will be explained later at its own paragraph.
At the preinitialize event of the page, we write the following in hex: < printh 23 02 50 01 >
< printh > is one of the few commands that parameter uses space char 0x20
< printh 23 02 50 01 > send four bytes: value < #2P1> hex: 0x23 0x02 0x50 0x01
@Arduino in its turn will send the data that we have specified through the switch case command in the Arduino code for this page which are the following:
CODE SAMPLE FROM .ino FILE

1.        Nextion.print("array_rows.val="); // Sending to Nextion the value of ARRAY_ROWS, that represent how many Lines is the array  
2.        Nextion.print(ARRAY_ROWS);        // We update the value of array_rows variable on Nextion, to be equal to ARRAY_ROWS   
3.        NextionEndCommand();   
4.              
5.        Nextion.print("n1.val=array_rows.val"); //Set the value of n1 numeric component, (no visible)  
6.        NextionEndCommand();   // to be equal to the value of array_rows variables on Nextion
7.  
8.        Nextion.print("t1.txt=\"");                
9.        Nextion.println("Welcome to message page"); //We send & print a message to the t1 textbox
10.  Nextion.print("there are ");      
11.  Nextion.print(ARRAY_ROWS);      
12.  Nextion.println(" NEW Message");              
13.  Nextion.print("TOUCH HERE TO READ");          
14.  Nextion.print("\"");  
15.  NextionEndCommand();  
16.  
17.  sending_text(0,0);           // to fuction <sending_text (byte nextion_var, byte cnt)> send 2 values one for each variable  
18.  sending_text(1,1);          // this is for sending and store the first 4 Lines on Nextion’s four first variables   
19.  sending_text(2,2);         // Nextion variables: va0, va1, va2, va3  
20.  sending_text(3,3);   // <sending_text(3,3);> this is going to store to the va3 on Nextion the array line 3 
21.              
22.  Nextion.print("cnt.val=3");    // After first 4 lines printed on Nextion, the <cnt> value is 3 and we update that on Nextion  
23.  NextionEndCommand();           // Remember that 4th line or place called with the number 3 (0,1,2,3,...19)

The first four lines have been stored to the vals and a message on the screen has also been sent and printed through the t1 textbox, which informs us for new message and the number of them by sending the value of ARRAY_ROWS.
The code will adapt automatically for maximum array Lines (places) as we define with ARRAY_ROWS at Arduino code and array_rows.val at Nextion to set all the code in Nextion for the max value of lines that are going to be showed. Example: , <h0.maxval> etc.
Textbox t1 is setted to multiline through its attribute and as you can see, the < print > and are working fine to change into new line, because < println > sends a carriage return character (ASCII 13, or '\r') and a newline character (ASCII 10, or '\n') at the end of the text.

//We get the same message result with the following:

Nextion.print("t1.txt=\"Welcome to message page\\rthere are "); 
         Nextion.print(ARRAY_ROWS);
         Nextion.print(" NEW Messages\\rTOUCH HERE TO READ\"");
         NextionEndCommand();

@A prompt “TOUCH HERE TO READ” on t1 box. When you press on t1, which covers all of the display, the touch event is going to print the first four lines, from the values we have sented, doing the following:

1. xstr 15,22,390,30,0,BLACK,34784,0,1,1,va0.txt  
2. xstr 15,22+30,390,30,0,BLACK,34784,0,1,1,va1.txt  
3. xstr 15,22+60,390,30,0,BLUE,34784,0,1,1,va2.txt  
4. xstr 15,22+90,390,30,0,BLUE,34784,0,1,1,va3.txt

We are using the < xstr > command that Prints text on the Nextion device using defined area for text rendering
usage: xstr ,,,,,,,,,,

We start to print with < xstr >, at 15 < x > and 22 < y > coordinates for the first line, and for the other three, we add 30 pixels to < y > coordinate from the last line’s < y > coordinate

@By pressing button b1, we print to the serial in hex form: < printh 23 03 4c 04 > and we also print one byte of the variable with: < prints cnt.val,1 >.
prints ,
is either component attribute, variable or Constant
is either 0 (all) or a number to limit the bytes to send

The result of < printh 23 03 4c 04 > and < prints cnt.val,1 > is the value: < #3L4Y > (where there is a “Y”, it is the value of the cnt variable each time). In hex: 0x23 0x02 0x50 0x01
is a counter egual to the number of the text array Line that we want to store into the Nextion's variable < nextion_var > for this example < va4 >.
According to our protocol, we have the command group “Line” (<#> <nextion_var> ). We send the two bytes, < nextion_var > and < cnt > to the void sending_text(byte cnt, byte nextion_var).
We are now waiting for the Arduino to send the array line that is equal to cnt value and store it to va4.
With the Release Event of b1 we change the values of the variables according to the above table at the introduction of this page(first_example) and we print them again.
The result is that we print a new line at the bottom and the other 3 lines go up a level.
At the release event of b2, the opposite things happen.

CODE SAMPLE FROM .HMI FILE

CODE SAMPLE FROM .ino FILE

/* < case 0x4C: > (0x4c = ‘L’) IF there is a matching with 'L' then we have the command group "Line" according to the protocol
                 * We are waiting 2 more bytes from the <nextion_var> and the <cnt>... (<#> <len> <cmd> <nextion_var> <cnt>)
                 * <nextion_var> is the Number of the variable on Nextion that we want to write
                 * <cnt> is the number of the text array Line that we want to store into Nextion's variable
                 * From Nextion we have sent < printh 23 04 4C 04 xx > where 04, in this example, the <nextion_var> and xx the <cnt>
                 * Same as the above, we are going to read the next 2 bytes as <uint8_t> and direct send them to
                 * < sending_text () > function as parameters to the local variables of the function 
                 * < sending_text(byte nextion_var, byte cnt) > that will be created on startup of the Function
                 */


void sending_text(byte cnt, byte nextion_var){

        /*From the Function < Nextion_serial_listen () > and <case 'L'> we have send the 2 bytes <nextion_var> and <cnt>
         * directly to this Function and run the function with the command <sending_text((uint8_t)Nextion.read(),(uint8_t)Nextion.read());> .
         * In this function we create 2 Byte local variables  and we assign the read bytes from case 'L' as their values 
         */
         
        /* From Nextion each time we press the <up> or <down> scrolling button we raise the variable on Nextion <cnt> by one cnt++ or decrease it by one cnt--
         *In this function we want to store the line from the text array that we ask with <cnt> to the variable va4 on Nextion
         * va4 is for this example we can write on every var that we will sent throw the <nextion_var>
         * the command for Nextion : va4.txt="text from the <cnt> Line of char text text[cnt]"
         */
    if(cnt < ARRAY_ROWS){
    
         Nextion.print("va");
         Nextion.print(nextion_var);
         Nextion.print(".txt=\"");
         Nextion.print(text[cnt]);
         Nextion.print("\"");
         NextionEndCommand();
    }
}


CODE SAMPLE FROM .HMI FILE

@< tm0 > is a timer with a setted time delay 300ms. It is enabled through < b1> press event and disabled by < b1 > release event. Every 300ms, it triggers the < click > command.
In the timer’s event, we write: click b1,0 click b1,1
<< click , >
is component’s .id or .objname attribute of component to refresh
1 to trigger Press Event, 0 to trigger Release Event.

The result of this is that when we hold down the < b1 > button, the text will scroll down continusly.
The same thing happens with < b0 >, only the text scrolls up continusly by < tm1 >.

More details on Page with ID = 2, or as we have named it, “second_example”:
@Each time the page loads, we send a command to Arduino through the preinitialize event of the page, to let the Arduino know on what page we are on.
For this page the command is: < 23 02 50 02 > in hex.
This command uses a specific protocol of our own. Its function on how we organize our commands and make Arduino identify them, will be explained later at its own paragraph.
At the preinitialize event of the page, we write the following in hex: < printh 23 02 50 02 >
< printh > is one of the few commands that parameter uses space char 0x20
< printh 23 02 50 02 > send four bytes: value < #2P1> hex: 0x23 0x02 0x50 0x02
@Arduino in its turn will send the data that we have specified through the switch case command in the Arduino code for this page which are the following:

CODE SAMPLE FROM .ino FILE

case 0x02:
            
    Nextion.print("array_rows.val="); // Sending to Nextion the value of ARRAY_ROWS, that represent how many Lines is the array
    Nextion.print(ARRAY_ROWS);          
    NextionEndCommand(); 
            
    Nextion.print("n0.val="); // Sending to Nextion the value of n0.val a numeric component that is hidden and used as variable on nextions code
    Nextion.print(ARRAY_ROWS - 3);          
    NextionEndCommand(); 
            
    Nextion.print("h0.maxval="); // setting the max value that Slider on nextion can take
    Nextion.print(ARRAY_ROWS - 4);          
    NextionEndCommand();
            
    Nextion.print("h0.val=h0.maxval"); //  setting the value of the slider to its max value ( pointer on the top)    
    NextionEndCommand();            
            
    save_text_to_variables_example2();
           
    break;

void save_text_to_variables_example2(){
  
  char temp_data[50];
  
  for(int i = 0; i < ARRAY_ROWS; i++){
    sprintf(temp_data, "va%u.txt=\"%s\"", i, text[i]);
    Nextion.print(temp_data);
    NextionEndCommand();
    delay(10);                           // Give some time to Nextion to read from Serial Buffer and Avoid buffer overflow
    
  }
}

That was a sample of the Arduino code. From now on, Arduino isn’t necessary. All of the following work with only the code on the Nextion



How to make components visible or invisible using command:
With < vis > command we can hide or show a specific component on the current page.

  • show will refresh component and bring it to the forefront layer.
  • hide will remove component visually, touch events will be disabled.
    When the object is hidden, we can get its value, or we can change it. Example: read or write a text from a textbox
    Syntax: < vis < component ID or name >, < state > >
  • < state > write 0 to make component invisible or 1 to make it visible
  • In page < first_example >, we make component n0 invisible, by writing: < vis n0,0 > with objectname or < vis 13,0 > with component’s ID
  • By writing < vis 255,0 >, we make every component in the current page invisible
    We give the command from the "preinitialize event" of the page that the component belongs by writing: < vis n0,0 >
    From Arduino, we write < Serial.print(“vis n0,0”); > and of course the 3 bytes for the end command < Serial.print("\xFF\xFF\xFF"); >

How to create a variable, change it to global scope and use it across different pages:
The Variable component is a non-visual component and is located below the Design Canvas. Variables are either 32-bit signed numeric or string content.
To create a variable, go to the “Toolbox” section and click on the “(X) Variable”. When pressed, a variable is going to be added.
From variable’s “Attribute”, change the < vscope > from local to global. “Local” is the default option when created.
From the < sta >, choose Numeric or String. Depends on how someone wants to use it.
When a variable is global, it can be accessed not only while the page they are in is currently loaded, but also while different pages are loaded.
We can access the variable with two ways:

  1. By giving the name of the page and the component’s < objectname >. Example: < home.va0.val >
    See the example at the < Preinitialize Event > of page < first_example > of this guide, where we use a variable created on page < home > named < curr_page_id>
    < home.curr_page_id.val=dp > We set the value of < curr_page_id > variable to be equal to with < dp > (< dp > is a system variable that returns the current page id).
  2. By using the b[.id] component array. Example: < p[.id].b[.id].val> .
    The above example for the page < first_example > gets the form < p[0].b[7].val=dp >.
    We prefer to use the second method, due to we can change the name of the page and objects freely. Be careful when changing page order, as the page ID will change too.

How to navigate between pages using < dp >, a Nextion’s system variable:
As you can see above, < dp > is a system variable that returns the current page ID when read the variable. E.g.: n0.val=dp.
And change page to value specified. E.g.: dp=1 // loads the page with ID=1.
In our example, we use the global scope variable < curr_page_id > to store the current page id.
With the navigation buttons, if we want to go to the previous page, we subtract one from the < curr_page_id >, by writing: < page p[0].b[7].val-1 > or < dp= p[0].b[7].val-1 >.
To go to the next page, we add one to the < curr_page_id >, by writing: < page p[0].b[7].val+1 > or < dp= p[0].b[7].val+1 >.
This is very useful when we want to return to the previous page after using a pop-up message.

How to make a useful pop-up message on Nextion:
To make a pop-up, we use a page with a transparent background, by setting the < sta > attribute of the page to “0” or “no background”, so when this page loads, we are going to have as a background the previous loaded page.
We can make a textbox, customize as we like and add text either from Arduino or from a local variable on Nextion. We could also add the text to the text attribute of the textbox component.
To leave from the message page, we can set at the Press Event of every object on the page AND on the page itself, to load the previous page with the following command:
< page p[0].b[7].val > or < dp= p[0].b[7].val > Thus, wherever we press on the page, we are going to go to the previous page.
We could also use a timer, that in its event we are going to run the same commands, after the setted time has passed. Then the pop-up message will disappear.
The timer also has to be setted to enabled, through its attribute < en > and it will start counting as soon as the pop_up page loads
NOTE: When the pop_up page loads, we don’t update the curr_page_id variable, as we do with the other pages.
In this way, we can go from the message screen to the previous loaded page, as the pop_up can be called from different pages.

To avoid the need of a page for every message, we created a global scope variable at the “home” page, with < sta=String > and < txt_maxl=200 >.
In this variable, we are storing the text we want to display on the pop_up message.
If from any object or any event on any page we want to display a message, we write the desired text to the global pop_text variable.
After that, we call the page pop_up.
E.g.:< home.pop_text.txt=”Test Line 1\rTest Line 2” > & < page pop_up > NOTE: \r for new line
In the pop_up page Preinitialize Event the text on t0 gets the text from the global variable < pop_text >, by writing: < t0.txt=home.pop_text.txt >.

How to use < sprintf > command:
A very useful command is the . This command will construct a string from different type of variables and will store it
in a char array in our case . We must declare the length of the array to be long enough to hold all the characters.
With sprintf we can include variables within a text using format specifiers with the prefix < % >, format specifiers are replaced
by the values specified in subsequent additional arguments.
sprint(char array to store value, "<TEXT TO PRINT %[format specifier]NEXT TEXT%[format specifier]",<name of 1 variable>,<name of 2 variable>)
Format specifier table from: https://arduinobasics.blogspot.com/2019/05/sprintf-function.html

More for sprintf-function to:http://www.cplusplus.com/reference/cstdio/sprintf/

How to change components’ attributes at Nextion from Arduino:
For every component on Nextion, we can chance only attributes that are showed with GREEN color when Nextion is “running”
with the following prototype .= . If attribute is text, the value must be inside <" "> quotes.
Supposing we want to send to component t3 the text < HELLO > the command for Nextion is < t3.txt="HELLO" >, from Arduino, we write < Serial.print("t3.txt="HELLO"") >;
When we type <"> means that <"> is a character and it's going to print it over serial as character. Else <"> is a STRUCTURE.
To change the font color t3, we write on Nextion: < t3.pco=BLUE > or < t3.pco=<31> >
From Arduino:< Serial.print(“t3.pco=BLUE”); > or < Serial.print(“t3.pco=31”); >
TIP: The exact names of the attributes and the values they can get, can be found on Nextion Editor at the attribute menu of any object.

How to use the return character < “\r” > on text boxes for new line:
In Nextion with the < \r > creates 2 bytes 0x0D 0x0A the first is the return character (ASCII 13, or '\r') and the second is the newline character (ASCII 10, or '\n').
We must send to Nextion the 2 bytes < \r > of the backslash () and character (r) to send the backslash char () we must add a () in front, for the escape character, due to we want to print the second () as character
E.g.: < Serial.print("t1.txt="Text Line 1\rText Line2\”"); >
When we are at Nextion we must include the < \r > inside " ", result: < "\r" >.
When it is inside the text there is no need for extra "". The quote marks of the text are enough.
Example 1 < t1.txt="LINE1\rLINE2" >
Example 2 < t1.txt="LINE1\rLINE2"+"\r"+t3.txt> we add a new Line with the text of t3 component.

How to initialize Nextion’s Serial baud rate:
NEXTION MUST HAVE THE SAME BAUD RATE WITH ARDUINO. For this we write at first page to the preinitialize event the command < baud=115200 >
NOTE "bauds" will change the default baud rate off 9600 until it changed again < bauds=115200 >. On the other hand, will change the rate until next start-up.
From Arduino: <Serial.print(“baud=115200”); > But first, we must have a Serial connection between Arduino and Nextion. This means that we have the same baud rate on both.
Second step is to change the baud rate of Nextion and then change the Serial baud rate on Arduino.

How to use the timer variable, a non-visible component:
After we select the timer from the toolbox, as this is a non-visual component, it will be added below the Design Canvas.
From timer’s event we can run specific code or commands in sequentially periods of the setted time.
This period of time can be setted by the < tim > attribute in milliseconds. We must start it by setting the < en > attribute to 1.
If we want the timer’s event not to repeat, we write at the end of all the commands of the event: < tm0.en=0 >. This disables the timer.
When timer is setted to < en=1 > from the attribute menu (when building the page), the timer will start and run with the load of the page.
To start the timer from a different time stamp, we have to set < en=0 > from the attribute menu (Nextion Editor) and enable it from an object’s Event with: < tm0.en=1 >
Examples on timers can be found on pages with ID=1, ID=3

How to add a numeric variable inside a text (same with Arduino):
From Nextion: We must convert the numeric to text with . For this, we must create a textbox (make it visible or not. It is up to you) to store the result of the
If the numeric value is the n0 and the t0 is the place we chose to store the result of the < covx >, then we write: < covx n0.val,t0.txt,0,0 >.
If t1 is the textbox we want to add the text and the result of the < covx >, < t1.txt="slider's Value"+"\r"+"h0.val== "+t0.txt >
In command, when length is fixed and value is less, leading zeros will be added

I almost forgot...

You can find the .HMI and .ino files of the tutorial at my website's Download section:
https://www.seithan.com/download-nt-ino/?

P.S. The site is certified with brand new SSL.

I think a good idea to add in this tutorial the answer that I give in the Quoted question, as a good examle on How to send values and store them on Arduino.

altitudeap:
not sure I have found my answer in the above so I would like to ask the similar question.. I have several pages and I use hotspots to move from page to page. I also want to know what page I am on. In the simulator I see that the output has a structure like this " 65 00 05 01 FF FF FF" What I have been able to discern is that the "05" is the page I have gone to and the "00" is the page I came from.
What serial commands will I use to parse this?

My application is this.

If going from page 0 to page 4. I want to populate a variable x with 540 and a variable y with 1 for direction. (driving a stepper)

if going from page 4 to page 0 I want to populate a variable x with 540 and a variable y with 0 for direction.

it may be a simple case statement of the following.
Serial Step Direction
case String x y
1 65 01 01 01 FF FF FF 790 0
2 65 02 01 01 FF FF FF 540 0
3 65 03 01 01 FF FF FF 1040 0
4 65 04 01 01 FF FF FF 1290 0
5 65 00 02 01 FF FF FF 790 1
6 65 02 02 01 FF FF FF 250 1
7 65 03 02 01 FF FF FF 250 0
8 65 04 02 01 FF FF FF 500 0
9 65 00 04 01 FF FF FF 540 1
10 65 01 04 01 FF FF FF 250 0
11 65 03 04 01 FF FF FF 500 0
12 65 04 04 01 FF FF FF 750 0
13 65 00 05 01 FF FF FF 1040 1
14 65 01 05 01 FF FF FF 250 1
15 65 02 05 01 FF FF FF 500 1
16 65 04 05 01 FF FF FF 250 0
17 65 00 06 01 FF FF FF 1290 1
18 65 01 06 01 FF FF FF 500 1
19 65 02 06 01 FF FF FF 750 1
20 65 03 06 01 FF FF FF 250 1

am I making this more difficult that needed?

How to send values and store them on Arduino.

Hi
The output structure that you see at the simulator is the return code of Nextion protocol No21 on section 7
used from the Touch Event of components when the < Send Component ID > is checked on or on .
It is in hex, hexadecimal format, and starting with 0x65.
Has 7 bytes length: 0x65 0x00 0x01 0x01 0xFF 0xFF 0xFF
First Byte 0x65 is the Format group of Nextion Return Data Touch Event
Second Byte 0x00 is the page number that touch event comes from
Third Byte 0x01 is the component ID
Fourth Byte 0x01 is the event that occurs < 0x01 for Touch Release Event > or < 0x00 for Touch Release Event>.
Next 3 bytes declares the end of the command.
You can find the above at Nextion instruction set:

As you have already understood it very difficult to assign the commands that you want in every Touch Event and much more difficult to read them separately for every event from the Serial and attach the function that you want on Arduino.

You must simplify this a lot, I’m going to help you on this.
First you must develop your own custom protocol so you can handle the commands that you need.

Second, sent to Arduino the command that you want through the Touch Event
Last, just read them.

As I can see your project needs to update 2 variables x and y or Step and Direction.
Let’s make a group command for this and named < Sent Variables >

We may assign the capital letter ‘V’ in this command group to identify it later.
The character ‘V’ is the hex 0x56 (google for Ascii character table).

We are going to use the following Data Format:
<#> <Step_var> < Direction_var >

  • <#> declares that a command is followed
  • declares the number of bytes that will follow (len = 3, is the one Byte < Step_var > and < Direction_var >is the other)
  • declares the group of commands (V = Sent Variables),you can add a different command group later.
  • < Step_var > is the value for the Step variable
  • < Direction_var > is the value for Direction variable

So as example for the case 1 of your project we must sent the number < 790 > as Step and the < 0 > as the Direction
Sending the following from Nextion Touch Event in hex :
< # 3 V 790 0 >

To send this trough Serial, we must write on < Touch Press Event > of the component, in your case the component is on < page1 > and has the ID < 1 >, the following:
< printh 23 03 56 316 00 >
Where the HEX is: <23 = #> <03=3> <56=V> <316=790> <00=0>
And that’s it.

So far, the problem is that you can’t send a number bigger than 255 in a single byte, that means that you can’t sent the <316> on hex = <790> in dec.

To do this we need 2 bytes, a high byte and a low byte and the things start to complicate.

In your project this is not a problem because as I can see the values can be divided with 10 and the result is going to be a number lower than 255,
that you can multiply it with 10 on Arduino code after the Serial reading.

At the end, You must sent: < # 3 V 79 0 >
With writing: < printh 23 03 56 4F 00 > Where is: <23 = #> <03=3> <56=V> <4F=79> <00=0>
You can find the HEX of a DEC number from on line tables or calculators.
Do the same with all the cases BE AWARE: uncheck the < Send Component ID >
Here is the code that you must use from Arduino:

//                                   ______________________________________________
                                    //--------CREATED BY SEITANIS THANASIS--------\\
                                   //---------------VERSION 13/9/2019--------------\\
                                  //----------------- www.seithan.com --------------\\
                                 //------ code for Step and Direction variables -----\\

#define Nextion Serial
  /* define that Serial will de writed as "Nextion" (when we write Nextion.print the compiler reads Serial.print)    
 *By defining the Nextion as Serial you can change at once the serial port at the whole code,
 * by changing the Serial to Serial1 for example
 */  

 int Step_var = 0 ;       // Variable to store the Step value that we sent from Nextion
 int Direction_var = 0 ; //  Variable to store the direction value that we sent from Nextion

void setup(){

 
  
  Nextion.begin(9600); // starting the serial port at 9600. NEXTION MUST HAVE THE SAME RATE. For this we write at
                         //  first page to the preinitialize event the command @  baud=9600  @ 
                         // NOTE "bauds" will change the default baud rate off 9600 until it changed again
  delay(500);
}


void loop(){

  Nextion_serial_listen();
  
  
 
}


void Nextion_serial_listen() 

{
    if(Nextion.available() > 2){                // Read if more then 2 bytes come (we always send more than 2 <#> <len> <cmd> <id>
        char start_char = Nextion.read();      // Create a local variable (start_char) read and store the first byte on it  
        if(start_char == '#'){                // And when we find the character #
          uint8_t len = Nextion.read();      // Create local variable (len) / read and store the value of the second byte
                                            // <len> is the lenght (number of bytes following) 
          unsigned long tmr_1 = millis();
          boolean cmd_found = true;
            
          while(Nextion.available() < len){ // Waiting for all the bytes that we declare with <len> to arrive              
            if((millis() - tmr_1) > 100){    // Waiting... But not forever...... 
              cmd_found = false;              // tmr_1 a timer to avoid the stack in the while loop if there is not any bytes on Serial
              break;                            
            }                                     
            delay(1);                            // Delay for nothing delete it if you want
          }                                   
                                               
            if(cmd_found == true){            // So..., A command is found (bytes in Serial buffer egual more than len)
              uint8_t cmd = Nextion.read();  // Create local variable (cmd). Read and store the next byte. This is the command group
                                             
              switch (cmd){
                                    
                case 'V': /*or <case 0x56:>  IF 'V' matches, we have the command group "Sent Variables". 
                           *The next byte  according to our protocol is the value for the <Step_var>
                           */
                     Step_var = Nextion.read() * 10 ;  // we read and store the byte at the variable

                     // The next byte  according to our protocol is the value for the <Direction_var>

                     Direction_var = Nextion.read();   // we store and store the byte at the variable
                     



                      Nextion.print("n0.val="); // Those lines is for the debug on Nextion
                      Nextion.print(Step_var);  // they sent the variables at the         
                      NextionEndCommand();      // numeric Component  n0 
                      
                      Nextion.print("n1.val=");    // Those lines is for the debug on Nextion
                      Nextion.print(Direction_var);     // they sent the variables at the     
                      NextionEndCommand();            // numeric Component  n1
                     
                  break;
                
                                
              }
            }
        }  
    }    
}

void NextionEndCommand()
{
    // with this two ways we can sent the 3 bytes so Nextion can understand the end of a command. Choose freerly.
    
  /* Nextion.write(0xff);
     Nextion.write(0xff);
     Nextion.write(0xff); */
  
    Nextion.print("\xFF\xFF\xFF");
  
 
}

As I have found some time, I made a .HMI file on Nextion Editor with the cases of your project.

In every button Touch Press Event you can find the < printh > command

By pressing on it, the command goes to Arduino, it stores the variables and Arduino returns the values to n0 and n1 numeric components for debug.

So, the system is working for sure.

If you want more information on Nextion displays, you are welcome to visit my site at:

to the Nextion Tutotial page:

The zip file < nextion_step_direction.zip > contains the .HMI file for Nextion editor

nextion_step_direction.ino (5.73 KB)

nextion_step_direction.zip (297 KB)

Thanks for sharing useful information.

(deleted)

Thank you for your kind words @orion444 and @cheerflora . I am trying to find some free time to add some new things that I have in mind, but schools, for me at least, have started.

Incredible information. Bravo Thanasi kai efharisto!

very good info thank you, question tho have you messed with DSbuttons? if so what code have you found that works?

You have a timer event on the nextion display whats that for? and how did you come up with the hex number for that?

Hello DRE50,

Can you please be more specific as I use many timers in the project?

And for the HEX, it is better to read through Serial.

Seithan:
Hello DRE50,

Can you please be more specific as I use many timers in the project?

And for the HEX, it is better to read through Serial.

I down loaded your relay project and i noticed you have a timer on the HMI file. i was wondering what it was for? and i having a hard time figuring out how you came up with the Hex for it?

You can see this abstract in the code:

You can read the comments

case 'S': 
   /* or <case 0x53:>  if 'S' matches, we have the command group "Nextion is Synchronized".
    * From the timer component on Nextion, we send every 4,9 seconds a < printh > command
    * so we check if Nextion is connected and synchronized.
    * If it is not, pin 12 turns HIGH as Alarm
    * We check the above, by writing in the timer's event: < printh 23 01 53 >
    */
      timeout_synq = millis(); // setting the timer to be equal to millis(), because Nextion sent a alive message
                  
                  
      break;
void check_Nextion_synq()
{
 if((millis() - timeout_synq) > TIMEOUT_FOR_SYNQ ) {
     digitalWrite(12,HIGH); 
     /* you could put a buzzer here (pin 12) to notify you that the connection is lost
      * for the LED, you will need a 220ohm in series.
      * Here we turn the buzzer or the LED on 
      */
    }else{
    digitalWrite(12,LOW); //stops the LED or the buzzer if synq comes back
    }
}

And all these because the page is going to remain the same for a long time, it is good to know if the connection between Nextion and Arduino remains and Arduino and Nextion are alive

I needed to build a pretty complex input panel to drive my application (a control panel for a stepper motor to move a table on a model railroad) and an Arduino program, along with an input panel on a Nextion display seemed to fit the bill. However, I'd never seen either piece of hardware until 3 weeks ago. I'm a long-time programmer but on mainframes, servers and PCs.

This forum was essential to getting my Arduino skills up and your very excellent Nextion tutorial was EXACTLY what I needed. It lets you send simple commands and make the Nextion do exactly what you want. I was surprised at how quickly I got things going. Contributions like yours, being offered for free no less, often go unnoticed and unheralded. I just wanted to tell you that you made my initial experience with the Nextion much less painless than it could have been.

I must confess to a basic bit of ignorance. I coded my Arduino program okay and got my Nextion panels (4 of them actually, linked with navigation buttons) all done up nice and they all worked fine using the Simulation mode of the Nextion Editor. Under simulation, I could actually move my stepper motor very precisely. However, when I got my real panel and wired it up, I was unable to reliably send and receive data between the Nextion and Arduino.

Fast forward to the final answer: being a non-electrical engineer I didn't realize that the GND from the actual Nextion panel needed to be connected to GND on the Arduino. Once I did that, all was fine. I'm sure this basic requirement is covered somewhere but I surely missed it.

That last mistake is all on me. Again, once I got that issue resolved, the application worked amazingly well. Thanks again, my friend.

1 Like