Nextion Code Generation
There are a lot of tools & gadgets available for the Arduino platform. One of them that is gaining interest is the Nextion TFT screens. Small TFT screens with resistive touchschreens and an editor to build the screen layout. These screens have their own intelligence taking away the burden of self programming TFT screens. However on the other side (i.e. Arduino) still needs to know all the components used on the screen to be able to communicate with them. So there is still quite some coding to do.
Nextion Editor
The Nextion Editor works with a project file (.HMI type) that contains all the information that is later compiled and flashed on the TFT screen. It contains all the possible components the screen supports and each component is capable of sending events when touched and released. There is also a debugging facility available that enables some simulation.
Coding
On the Arduino side each button, textfield or any component needs to be defined in the Arduino sketch. Also for each event generated by the screen a callback needs to be defined to respond to that event. Last but not least, in the loop a function needs to be called (NexLoop()) that listens to the screen (through the serial port) and calls the designated callback function. That is still quite a lot of coding to do. Would it not be nice if all that code can be generated from the screen itself.
Solution
The project file contains everything we would need to generate the code. After all if we open the HMI file in the editor, all the objects, layout, events etc are shown. So why not hack the HMI file to get al the data from the HMI file and generate all the required code for the Arduino Sketch. So in general the flow would be:
Nextion Editor -> HMI file -> Arduino code -> Verify -> Upload.
As you can see in the diagram there is defined the Nextion Include Generator. It is actually more than just in .h generator. The current version will generate all code for the Arduino into a working sketch. All the definitions, callbacks and nex_listen_list are generated. But als (if requested a working sketch). I am using a decoupling mechanism to decouple the callback functions. Why? I we were to regenerate all callbacks would be replaced. So the callbacks call a user function (OnRelease.. and OnPush..) which is put in a separate .ino file. The user should edit these user functions and not the callbacks themselves.
A simple example. Below is a very simple screen with 2 buttons, a progress bar and a textfield. The buttons will generate a pop event and the page will generate a push event
Save this file and now it is time to generate our code.
I developed an application (using Code::Blocks and wxWidgets) that can read our HMI file and generate the code for a complete Arduino sketch. Below is the main (and only screen) that controls the whole generation process. When you start it the first time, It will complain about a missing settings file. This correct. It will be generated automatically. Once the display shows, go to Settings->Program settings. This sets some parameters for visualization, Arduino sketch folders and Postfix or Prefix settings with structure names.
Settings
Page Structure Prefix: When a structure is generated, this text will be put in front of it
- Page Structure Postfix: This will be put after a structure name
- Color settings : Both Screen and Generate button can be set
- Default settings: Not implemented yet
- Arduino sketch folder. Set this to your Standard Arduino folder. If you are working with the Arduino-Eclipse IDe (currently Sloeber 4.0) set this folder to your Workspace folder.
Save will make the changes permanent. Cancel or X will leave all unchanged.
Mainscreen
Below is a typical screen from the program. In this case set to our example.
On the left there is a filtered treeview showing only the folders and possible HMI files. If a HMI file is selected it is validated as an HMI file (made with Nextion Editor >= version 0.38). also in column 3 all the objects that the HMI file contains are shown.
The second column is the output folder. If the checkbox “Synchronize output folder” , the treeview will follow the input treeview. Any folder may be selected. Realize though that using the checkboxes on the screen, this may automatically change.
Checkboxes
There are 4 checkboxes:
- Synchonize output folder: synchronizes with the input folders
- Generate User functions: will generate a userfunctions file
- Specific for Arduino IDE: Will switch the output folder to the Arduino sketch folder as specified in the program settings
- Create Arduino sketch or program: Will generate an Arduino sketch or a program for the Eclipse-Arduino IDE (Sloeber).
Generate
If valid conditions are set, code files are generated. For this example the following code is generated in the Arduino sketch folder. Also a folder is created with the same name as the sketch. According to the Arduino IDE specifications.
#ifndef DEMOSCREEN_HMI_H
#define DEMOSCREEN_HMI_H
//
// include file generated from HMI file demoScreen.HMI
// HMI file was created with Nextion IDE version V0.43
// generated on 2017-03-01 13:25:54
//
#include
#include
/**
* Structure of page: page0
*/
struct {
NexPage page0 = NexPage(0, 0, “page0”);
NexButton b0 = NexButton(0, 1, “b0”);
NexButton b1 = NexButton(0, 2, “b1”);
NexProgressBar ProgressBarr = NexProgressBar(0, 3, “ProgressBarr”);
NexText TextField = NexText(0, 4, “TextField”);
} pg_page0;
void OnPush_page0(void *ptr);
/**
* @name page0_PushCallBack
* @param ptr Pointer to page0 object that generated this event
* is called when a push event is generated from NexPage page0
*/
void page0_PushCallBack (void *ptr) {
OnPush_page0(&ptr);
}
void OnRelease_page0_b0(void *ptr);
/**
* @name page0_b0__ReleaseCallBack
* @param ptr Pointer to b0 object that generated this event
* is called when a release event is generated from NexButton b0
*/
void page0_b0_ReleaseCallBack (void *ptr) {
OnRelease_page0_b0(&ptr);
}
void OnRelease_page0_b1(void *ptr);
/**
* @name page0_b1__ReleaseCallBack
* @param ptr Pointer to b1 object that generated this event
* is called when a release event is generated from NexButton b1
*/
void page0_b1_ReleaseCallBack (void *ptr) {
OnRelease_page0_b1(&ptr);
}
void OnRelease_page0_ProgressBarr(void *ptr);
/**
* @name page0_ProgressBarr__ReleaseCallBack
* @param ptr Pointer to ProgressBarr object that generated this event
* is called when a release event is generated from NexProgressBar ProgressBarr
*/
void page0_ProgressBarr_ReleaseCallBack (void *ptr) {
OnRelease_page0_ProgressBarr(&ptr);
}
/**
* @name attachCallBacks()
* attaches our callback functions to the Nextion system
*/
void attachCallBacks() {
pg_page0.page0.attachPush(page0_PushCallBack, &(pg_page0.page0));
pg_page0.b0.attachPop(page0_b0_ReleaseCallBack, &(pg_page0.b0));
pg_page0.b1.attachPop(page0_b1_ReleaseCallBack, &(pg_page0.b1));
pg_page0.ProgressBarr.attachPop(page0_ProgressBarr_ReleaseCallBack, &(pg_page0.ProgressBarr));
}
/**
* @name nex_listen_list
* is a list of all objects that need listening to
*/
NexTouch * nex_listen_list[] {
&pg_page0.page0,
&pg_page0.b0,
&pg_page0.b1,
&pg_page0.ProgressBarr,
NULL
};
#endif
[/code]
Let’s take a closer look that the generated code of our .h file.
- An anonymous struct is created with a prefix “pg_” as specified in the program settings. This struct contains all objects on that page.
- for each event, a prototype of an ‘OnPush” or “OnRelease” is created.
- for each event a callback is created calling the user function defined above.
- a function attachCallbacks() is created taking care of all callback functions
- a nex_listen_list array is created of all objects that need listening to.
The next file is the userEvents. UserEvents are called from the callback functions so that we can decouple functionality from generated code. For the arduino wet get in the case of our example, the following code:
As you will notice, only the HMI objects that actually generate an event will have a userEvent.
And finally the sketch:
[code language=”cpp”]
/**
* program : MyDemo.ino
* generated by NextionIncludeGenerator
* generated on 2017-03-01 13:25:54
*/
#include <Arduino.h>
#include <Nextion.h>
#include “demoScreen.h”
SoftwareSerial HMISerial(9,10);
/**
* @name setup()
* Is called once during program startup
*/
void setup() {
nexInit();
attachCallBacks();
}
/**
* @name loop()
* Is called continuously
*/
void loop() {
nexLoop(nex_listen_list);
}
[/code]
I use SoftwareSerial for the communication with the Nextion screens. This can be set in NexConfig.h within the library.
With the generated code, a complete sketch is made without any coding at all.
Risks
The .HMI file is propriety and this makes it difficult to foresee the future as far as versions are concerned. The current build can execute HMI files from V0.38 up to V0.43 (current version). Should the layout change I will try to fix it for future versions as well.
Current status
The project is in final testing stage. I have some testers around the globe who are testing the product. Most changes/defects now are from a cosmetic perspective.
Hello, I would like to work with the code, either to help test it or to use it, from Nairobi, Kenya.
Hi,
please i want to ask how can i get the program for generating the code for nextion
thank you in advance