Code Generator for Nextion Screens

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 <Arduino.h>
#include <Nextion.h>
/**
 * 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

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:

#ifndef DEMOSCREEN_USER_EVENTS_HMI
#define DEMOSCREEN_USER_EVENTS_HMI

//
// user function 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 <Arduino.h>
#include <Nextion.h>
#include "demoScreen.h"

/**
 * @name OnPush_page0
 * @param ptr Pointer to object generating the event
 * this is the user function corresponding to the push event
 */
void OnPush_page0(void *ptr) {
}

/**
 * @name OnRelease_page0_b0
 * @param ptr Pointer to object generating the event
 * this is the user function corresponding to the release event
 */
void OnRelease_page0_b0(void *ptr) {
}

/**
 * @name OnRelease_page0_b1
 * @param ptr Pointer to object generating the event
 * this is the user function corresponding to the release event
 */
void OnRelease_page0_b1(void *ptr) {
}

/**
 * @name OnRelease_page0_ProgressBarr
 * @param ptr Pointer to object generating the event
 * this is the user function corresponding to the release event
 */
void OnRelease_page0_ProgressBarr(void *ptr) {
}

#endif

As you will notice, only the HMI objects that actually generate an event will have a userEvent.

And finally the sketch:

/**
 * 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);
}

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.

 

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Verplichte velden zijn gemarkeerd met *