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:
- Remove the need for the external device
- Reduce the load on the external device
- Use a smaller cheaper external device
- 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