How to use the Reparse mode

Since there is no help on using the reparse mode I have written a document to try and make it easier to understand how the reparse mode works and how to use the timers as sub programs. It also has a lot of comments and instructions on how to use the nextion language using my inverter code as the example.

Writing code on the Nextion

By writing code on the Nextion instead of another external micro controller you can:

  1. Remove the need for the external device
  2. Reduce the load on the external device
  3. Use a smaller cheaper external device
  4. Use the faster more powerful 32 bit micro controller on the Nextion

When I first started my project with the Nextion to make a remote touch screen control for my off grid solar inverters I started by programming an Atmel Mega 1284 with Bascom a compiler for the AVR that uses an easy to use basic like language with built in libraries written in assembler that I have been using for many years I got this working.

After looking at the Nextion’s built in language I decided I would try to make it work solely on the Nextion display , the inverters use RS232 at 2400 baud so with a RS232 level converter I could plug the Nextion into them, not having used the C language before which I have always found to be hard to follow as its not written in plain English (the very reason Basic was invented ) it took me a long time to get going.

I met some road blocks on the way.

  • I had to write the code to generate x modem CRC16 as this is what the inverters use.
  • I had to work out how to create sub routines as the Nextion language has none.
  • I had to work out how to use the reparse mode to read the data from the inverters
  • I had to work out how to parse (read and decode) the block of data sent from the inverter.
  • I needed animation

I have done all of this and can now control my inverters with the Nextion.

So now in an effort to help others and document this I am going to go through what I have done.


First reparse mode and how to use it.

You set the Nextion into reparse mode with the command recmod=1 and disable it with recmod=0 when you do this the receive buffer is cleared.

Here is an example of code using this mode

First I have send a request to the inverter so this timer is enabled when the Nextion page is selected.

//TIMER QPGS0
recmod=0  //turn off reparse mode clear buff
recmod=1 //turn on reparse mode
// Clear fields
data.txt=""
//result.txt="Waiting for reply"
// Send Command
//QPGS0
printh 51 50 47 53 30 3F DA 0D
// Enable TimeOut timer
timout.en=1
// Enable ReadBuffer timer
tm7.tim=3000
tm7.en=1
qpgs0.en=0 //disable QPIG0 time out timer will re-enable

The inverter gets the command QPGS0 and will then send back the various voltages , currents

Here is what is returned:

(1 92931501199999 L 00 234.7 50.00 234.7 50.00 0727 0685 017 51.2 000 086 082.8 000 01476 01448 018 10100010 1 3 050 120 02 00 000˜–

the “(“ is the start character
the “1” is the inverter number
the 92931501199999 is its serial number
the “L” is the mode, L is line mode

From this you can see that a space separates the various readings that are sent back.

Note if you design the system that sends data back like this you will find it much simpler to parse your data.

Now we want to check for data in the buffer.

So we need a variable to copy the buffer to work with the data, then we could clear the buffer ready for new data if we wanted to.

You can see now how timers are used as sub routines by enable and disabling them.

This command will return data from the string starting at the space character specified that is the first, second, third, forth and so on occurrence of the space character or the one used in your data.

spstr Split String
usage: spstr <src>,<dest>,<key>,<index>
<src> is src .txt attribute or string data constant
<dest> is .txt attribute where result is stored
<key> is the text delimiter encapsulated in double quotes
<index> is zero-indexed iteration result to return

//TIMER 7
// Check the ReadBuffer
qpgs0.en=0  //turn off timer that sent the command
if(usize>=3)  // this takes into account the  “(1 “ 3 characters with the space
{
  usize=usize-3  // we do not need the header
  // Status update
  // Data received
  ucopy data.txt,1,usize,0  //copy all data from buffer to the variable data.txt
  spstr data.txt,serial.txt," ",1  //  this returns the serial number
  spstr data.txt,gridvolts.txt," ",4 // this returns the grid voltage 234.7 in the example above
  spstr data.txt,gridfreq.txt," ",5  // this returns the grid frequency  50.00 in the example above
  spstr data.txt,outvolts.txt," ",6 //this returns the output voltage 234.7 in the example above
  spstr data.txt,outfreq.txt," ",7
  spstr data.txt,outwatts.txt," ",9
  spstr data.txt,load.txt," ",10
  spstr data.txt,battvolts.txt," ",11
  spstr data.txt,va16.txt," ",12
  covx va16.txt,va17.val,0,0 //convert the text to a number variable
  if(va17.val>0) //charging battery
  {
    spstr data.txt,battamps.txt," ",12
  }
  spstr data.txt,va16.txt," ",26
  covx va16.txt,va18.val,0,0  //convert the text to a number variable
  if(va18.val>0) //discharging battery
  {
    battamps.txt=""
    battamps.txt="-"
    battamps.txt+=va16.txt
    strlen battamps.txt,va18.val  //find the length of  battamps.txt string variable put value in  va18.val variable
     if(va18.val==0&&va17.val==0)//if both variables are zero
  {
    battamps.txt="0"
  }
  spstr data.txt,soc.txt," ",13
  spstr data.txt,panlamps.txt," ",25
  spstr data.txt,panvolts.txt," ",14
  spstr data.txt,va16.txt," ",2
  if(va16.txt=="L") //here we see what mode the inverter is in if its “L” its line mode
  {
    va12.val=1  //setting this to one sets some animation running
  }else
  {
    va12.val=0
  }
  // Disable timers
  timout.en=0 //this is a time out timer which will stop all the other timers if we do not get data
  tm7.en=0
  // Disable Protocol Reparse Mode then enable it (this clears the buffer)
  recmod=0
  recmod=1
  // now send a command for the data from the second inverter
  //QPGS1  CRC16
  printh 51 50 47 53 31 2F FB 0D //  FB  0D  is the CRC16
  data.txt=""
  timout.en=1
  tm8.tim=3000 //give the inverter time 3 seconds to send data back at 2400 baud
  tm8.en=1
}

Here we do the same as above for the data from inverter two
again it can be seen how the timers are used as sub routines by enabling the timer

//TIMER 8 QPGS1 PARSE
// Check the ReadBuffer
//covx usize,buffersize.txt,0,0
if(usize>=3)
{
  usize=usize-3
  // Status update
  // Data received
  ucopy data.txt,1,usize,0
  spstr data.txt,serial2.txt," ",1
  spstr data.txt,outwatts2.txt," ",9
  spstr data.txt,load2.txt," ",10
  spstr data.txt,va16.txt," ",12
  //here we are converting to a number it is in text format in va16.txt
  //but is needed in a number variable format so we can do mathematical
  //type decisions on it

  // covx <src>,<dest>,<length>,<format>
  //<src> is text attribute (or numeric attribute when <dest> is text)
  //<dest> is numeric attribute (or text attribute when <src> is numeric)
  //<length> will determine if leading zeros added to conversion to text
  //<format> 0: integer, 1: Comma separated 1,000s, 2: Hex

  covx va16.txt,va17.val,0,0
  if(va17.val>0) //charging battery
  {
    spstr data.txt,battamps2.txt," ",12
  }
  spstr data.txt,va16.txt," ",26
  covx va16.txt,va18.val,0,0
  if(va18.val>0) //discharging battery
  {
    battamps2.txt=""
    battamps2.txt="-"
    battamps2.txt+=va16.txt
    strlen battamps2.txt,va18.val
   
  }
  //inverter running from grid
  if(va18.val==0&&va17.val==0)
  {
    battamps2.txt="0"
  }
  spstr data.txt,panlamps2.txt," ",25
  spstr data.txt,panvolts2.txt," ",14
  // Disable timers
  timout.en=0
  tm7.en=0
  tm8.en=0
  tm9.en=0
  // Disable Protocol Reparse Mode (this clears the buffer)
  recmod=0
  recmod=1
  //QPIGS
  printh 51 50 49 47 53 B7 A9 0D
  data.txt=""
  timout.tim=5000
  timout.en=1
  tm9.tim=3000
  tm9.en=1
}

Hopefully that set of timer events shows you how to use the reparse mode.

Now generating an x modem CRC16, this took me some time to get working efficiently.

You need a text variable to store all the ASCII characters your likely to use in your data so you need to set its txt_maxl value large enough to hold all of them as I was only using character 32 to 126 I set it at 100.

You will see in the code because I start at 32 this has to be accounted for.

You also need a text variable in which to put the text to be worked on va6.txt in my code.

The code is stored in in timer crcgen so all that needs to be done is to put the text in va6.txt and enable timer crcgen

// TIMER crcgen
//generate CRC16
//va6.txt contains command
strlen va6.txt,count.val //get length of string
NUM1.val=0 //start a first character
crc.val=0
k.val=count.val
n1.val=count.val
count.val=0
while(k.val>0)
{
  //substr <src>,<dest>,<start>,<count>
  char.val=0
  substr va6.txt,TXT1.txt,count.val,1
  NUM1.val=1
  l.val=0
  //spstr <src>,<dest>,<key>,<index>
  spstr ASCII.txt,SPLIT.txt,TXT1.txt,0
  strlen SPLIT.txt,num1.val
  char.val=num1.val
  char.val+=32  // because I started at character 32 I need to add 32 to the value
  n4.val=char.val //character number
  char.val=char.val<<8
  crc.val=crc.val^char.val
  i.val=8
  while(i.val>0)
  {
    j.val=crc.val&0x8000
    if(j.val==0x8000)
    {
      crc.val=crc.val<<1
      crc.val=crc.val^0x1021
    }else
    {
      crc.val=crc.val<<1 //left crc shift 1
    }
    crc.val=crc.val&0xFFFF
    i.val-=1
  }
  count.val+=1 //move to next character
  NUM1.val+=1 //increment by 1
  k.val-=1 // decrement by 1
}
// this extracts the two bytes from the 4 bytes of the variable else you get two zero bytes
// sent with the crc when you try to send the CRC value and its reversed
//covx num1.val,tex10.txt,0,0
n0.val=crc.val
num1.val=crc.val
crc.val=crc.val>>8 //this moves the first byte in the crc value to the right 

prints va6.txt,0  // send the original command to serial port
prints crc.val,1 //now the first crc byte is in position 1 as we moved it above
prints num1.val,1//we copied the original crc value to num1.val which had the second byte in position 1
printh 0D //send carriage return
crcgen.en=0
va8.val=5
readbuf.en=1

Animation

Now how to do animation.

You need to make images to create the effect these are done in the same way that it was first done before computers one frame at a time so with a program like paint you create your images, you then load them into the Nextion editor you put a picture box on the display then select your first image in the pic setting now select a timer and a variable from the tool box.

Note there are 28 pictures

This code goes in the timer

p0.pic=va0.val //we select our first picture from the variables value
va0.val+=1   //we add one to the variable 
if(va0.val>28) //if the value is grater than our last picture number go back to the first
{
  va0.val=0 //go back to the first
}

     I have an HMI file you can run in the nextion editor demonstrating this it also has a dual state button that starts and stops the animation.

   Button code

if(bt0.val==1)
{
  bt0.txt="STOPED"
  tm0.en=0  //turn off Animation
}
if(bt0.val==0)
{
  bt0.txt="RUNNING"
  tm0.en=1 //turn on Animation
}

If this code was put into a timer and you changed the button value from an external source the text would change on the button

2 Likes

HMI Animation example and word document here
https://forums.aeva.asn.au/viewtopic.php?f=64&t=6788

This is a really nice guide @paulvk and thank you for taking the time to write this all up and share it with us! I took the liberty of editing the OP to fix the formatting for the forums, no text or code has been changed. I hope you’re OK with this, it’s such a great guide I felt it necessary to make sure it’s presented well so everyone can read it.

Happy to have it improved in any way Nextion’s examples are of very limited help they do not assume little experience in writing code.

Hi. I am using reparse mode in my project. It works very well for me. The most important thing is that you don’t need additional MCU.

Instead of turning off and on reparse mode You may use udelete command to clear buffer.

Yes I now know but as I said I was learning along the way the code is from last year.
Its important though that people can see that the nextion is much more than the displays they have been use to. I see many asking for a library which to me does not make any sense. I will post some more instructions/help on how to use the reparse mode and simply send a block of data to the display letting the display work out what attributes need to be changed , also using the xmodem crc to check the data is ok , I know the display has a crc function but I do not think its as versatile as my xmodem code or as easy to use.
Also I worked out a way to create a global array but it takes a lot of ram as it was an exercise in finding a way to keep the data for waveforms now knowing this the 7 inch intelligent series would have been my choice instead of the enhanced.
Perhaps you could post some code snippets to help people along.

Its important though that people can see that the nextion is much more than the displays they have been use to. I see many asking for a library which to me does not make any sense.

While I certainly appreciate your effort you put into this guide I don‘t agree with what you‘re implying here. It‘s actually one of the not-too-frequent cases where I agree with the guys at Nextion (though maybe for other reasons): Nextion is not a replacement for a microcontroller - and I‘m not even talking about things like missing ADC etc.
Yes, there are a lot of things you can do on a Nextion. But that does not mean you should do it - for various reasons. The two main points IMO are

  • Performance. Nextion is actually an interpreted language: your code doesn‘t run directly on the CPU. Instead it is - without any optimizations - executed by an interpreter at runtime. That‘s why you can send commands over serial, too. They‘ll be interpreted just like the other code. While I haven‘t done any benchmarks, this is easily an order of magnitude slower than native execution - as you‘d have on a separate microcontroller. For reference, have a look at Micropython. In case you don’t know, it allows you to run a Python interpreter on your microcontroller. Gives you a lot of flexibility but also costs a ton of resources. Also remember that you have less than 4kB RAM at your disposition on most Nextions. That‘s 8bit Atmega level.
  • Severely restricted environment. You‘re programming in a language with little features (no arrays, no real functions, no nested math (no ()), no ord(), …). On top of that you can‘t use any libraries either. There are for sure platform independent C/C++ libraries for things like CRC that you can compile for any microcontroller (there are even things like FAT32 file system drivers). For Nextion however you have to do pretty much anything from scratch and work around all the limitations named above. Simple example: there’s no math library. If you need a square root, you got to implement your own square root algorithm (I had to do that). After spending much time on writing, testing and optimizing one (!) basic math function, you have the joy of copying it to every page where you need it because there are no global functions or something like includes. All that is just the language itself. On top of that you got the limitations of the IDE and the debugging possibilities.

Nextion is fine for the GUI stuff. All the other features are little more than an add-on, intended for light usage. It’s not what Nextion has been designed for.
Many projects require a microcontroller anyways and in that case I think you should use it whenever possible. Most tasks that can be done on either the micro or Nextion are likely (much) easier to implement and faster in exection on the microcontroller.
Even if you could get rid of the microcontroller, is it worth it? How much more time do you have to spent to get your project working on Nextion only? Or in other words, why would you want to work within the constraints named above if you don‘t have to?

What I certainly don‘t want to promote is the extensive usage of the Nextion library for Arduino or any similar library. Thinks like changing a variable on Nextion if a button is pressed is something that should be done inside of Nextion without taking the slow way over serial to the MCU and back over serial to the Nextion. Using the library is not bad by itself but the amount of serial communication should be minimized. Not only is the bus itself very slow (running at 10s or 100s of kHz while Nextion and MCU run at 10s of MHz), every command in either direction needs to be created on the TX side and parsed on the RX side - meaning it always costs time on both devices.
I also don‘t want to say that there were no projects which are fine without a microcontroller. There certainly are; I just don‘t think they‘re the majority.

Kind regards,
Max

Thank you Max for your long reply I think awareness of the capabilities is important and that is what I was trying to achieve. Looking at the hobbyist area I think there is a lot of scope for the nextion to do a lot of work but I have seen many instances where lots of data is flowing over the serial link that requires a lot of code in the external microcontroller and people using high baud rates to keep up with the flow , I started this way , wrote the code on the external microcontroller had it working. I then started to look at the nextion language reference and then from experience working with my solar units I could see that much efficiency could be gained by sending a block of formatted data and then working what it meant so reducing the amount of work on both sides of the serial link . This required the reparse mode and then this ended up with me doing without the external microcontroller , as the units use xmodem crc I then had to get the nextion to do this so that commands sent to them would be accepted. Next I am going to put together a guide to use reparse mode and formatted data to control the display. As far as the speed of the display for me running animation , generating crc getting the values from that block of data updating text , converting to numbers and using the numbers to calculate totals has not shown any sluggishness on the part of the display (7 inch enhanced).

Regards Paul