The Sunday Blog: Talking to your Nextion HMI

Part 8: Moving on with the new class

Hooray… this is the 30th edition of the NEXTION Sunday blog! But to our big surprise, it was the one published last week, talking about wrapping the Nextion communication functionality in a simple C++ class which had the double number of readers than other blog episodes during the first 7 days. Obviously, there are many people interested in having a simple .h file accompanying their main .ino (or .c or .cpp) file instead of linking a highly complex, heavy and specialized library. Enough motivation to move on!

But before…

…let’s resume what we did last week: We wrote a template typed C++ class to handle the communication between our MCU (Arduino, STM, Teensy, etc.) and one or even multiple Nextion HMI displays with the opportunity to use different serial port types: The classical hardware serial ports, serial ports emulated by software (SoftwareSerial or AltSoftSerial), and virtual serial ports which are mapped over USB.

Ideally, we would use the default serial port, called just “Serial” which is on pins 0 and 1 and which is duplicated over USB on Arduino UNO and MEGA for debugging, since it is directly connected to the serial monitor of the Arduino IDE. Thus, on the Arduino MEGA, we may connect our Nextion HMI to RX1 and TX1, the hardware serial port called Serial1. On the Arduino UNO, there are no further hardware serial ports, thus we have to emulate one in software and determine the pins for connecting the Nextion. On NXP Kinetis series MCUs like the Teensy LC or 3.x series, things are agin different: Serial is purely virtual over USB towards the IDE, while on pins 0 and 1, there is an independent hardware Serial port called Serial1. Confusing, isn’t it? Lets look at a few connexion variants using an example :

Today’s example project

On our Nextion HMI, we need a number field, called n0 which might display values from 0 to 100, and a progress bar j0 which shall follow the displayed numbers. To keep things interesting, we want the color of the progress bar to be red when the values are between 0 and 49, yellow from 50 to 74 and green above 75, but all that will be controlled by the MCU. The HMI file can be downloaded here: graphdemo.HMI. In the Arduino IDE, we open a new project and add immediately a second tab which we name newNextion.h. Then, we copy the content of this file into it: newNextion.h and we are ready to work on our main sketch.

The beginning of the main sketch might look differently for different MCUs as stated above. Let first use the Arduino MEGA where we have a second hardware serial port Serial1 for the Nextion on pins 18 and 19, while we use Serial for debugging. Thus, we initialize the communication as follows :

#include "newNextion.h" 
NexComm<HardwareSerial, HardwareSerial> nex1(Serial1, Serial)

For the the UNO, things are slightly more complex, we have to use the SoftwareSerial library to create a second port for the Nextion before we can initialize our NexComm object accordingly. The SoftwareSerial port has naturally to be created before we define NexComm, this time with SoftwareSerial as first template parameter:

#include "newNextion.h" 
#include <SoftwareSerial.h>

SoftwareSerial mySerial(2, 3); // RX, TX
NexComm<SoftwareSerial, HardwareSerial> nex1(mySerial, Serial);

Not much more complicated and rather simple, isn’t it? Finally, for Teensy LC and 3.x MCUs, we can use pins 0 and 1 since these are HardwareSerial Serial1 and independent from Serial which is an USB serial port:

#include "newNextion.h" 
NexComm<HardwareSerial, usb_serial_class> nex1(Serial1, Serial);

The  remaining sketch is pretty straightforward and identical for all serial port configurations above. That’s why one might call our NexComm object a hardware abstraction layer (HAL). In setup() we create a loop with i running from 0 to 100. First, we determine the range start for the different progress bar colors and set it accordingly. Afterwards, we update the component with the i value and that’s all:

void setup() {
  String cmd;
  nex1.begin();
  for(uint8_t i=0;i<=100;i++)
  {
    if(i==0)
    {
      cmd = "j0.pco=";
      nex1.cmdWrite(cmd + 32768); //red
    } else if (i==50)
    {
      cmd = "j0.pco=";
      nex1.cmdWrite(cmd + 33792); //yellow      
    } else if (i==75)
    {
      cmd = "j0.pco=";
      nex1.cmdWrite(cmd + 1024); //green      
    }
    cmd = "j0.val=";
    nex1.cmdWrite(cmd + i); 
    cmd = "n0.val=";
    nex1.cmdWrite(cmd + i);
    delay(50);
  }
}

void loop() {
  // nothing to do here
}

Now, like me, you may find it bothering, that since we have only the generic cmdWrite() function of the NexComm object, we are required each time to build the full command string before we can send it. Wouldn’t it be nice to simplify that? The classic Nextion libraries create a different object type for each different GUI component type with all the available attributes as properties. That makes these very complex and they need to be reworked each time a new component type is added to the Nextion Editor. The current official Nextion library has for example not yet support for the xfloat component… In addition to that, several components see their attribute set modified depending on another attribute: Let’s look at the page component and its sta attribute. If it’s set to “no background”, no further related attributes are available. But when we set it to “solid color”, a bco attribute is available. And finally, when we set sta to “image”, the bco attribute is not longer available, but at its place, the pic attribute appears. How can all these special cases and exceptions be handled? I’ll suggest a much simpler approach in the next blog article.