Lately I’ve been playing around with the Nextion HMI 2.8″ display from Itead studios. If you can get past the chinglish in their documentation it can be pretty fun and interesting little display. It provides RS232 at TTL for communication between a MCU and the display. There are some quirks, as we will see.
The editor software for the display only runs on windows, and is very limited and somewhat confusing. Writing to the display can be done in two ways, direct RS232 to the display (which according to some forum posts, can be rather tedious process) or upload to the display via µSD card.
Since I have a mac I use Windows 10 virtual machine for editing the display layout and then upload to the display via µSD card. Annoyingly enough, mac for some reason always creates ._NameOfFile.tft when uploading to the µSD card, apparently it’s something for spotligh for search indexing. This drives the display mad, and it took quite a while for me to realise what was going on when the display complained about multiple .tft files on the card. So, I went through terminal, navigated to the root folder of the card and just deleted that file… You can see where I’m going with this, it’s pretty time consuming when you are constantly updating the layout. So, I wrote a short script to handle this stuff for me. It’s pretty savage but pretty neat.
#!/bin/bash/
/bin/df -h | grep K-30
/bin/cp /Users/username/path/to/HMIfile.tft /Volumes/YourSDCard
/bin/rm /Volumes/YourSDCard/._HMIfile.tft
/usr/sbin/diskutil unmount /Volumes/YourSDCard/
echo ‘Success’
Could use some improvement, but it works and saves time which can be used for something more important, like petting my cats.
The HMI display spits out hex code for push actions, which makes it pretty easy to read whatever is coming from it, for example if you want a button to let’s say, turn on a LED on an Arduino. But timing is critical, and especially if you are not using interrupts on the AVR so don’t let your loop do too much apart from just waiting for serial data coming from the display. Since I’m playing a lot with Arduino Nanos, I want to use software serial for the HMI, and use the primary serial for debugging and uploading sketches. Here’s my code snippet of how I read the data from the display:
// We add the library software serial
#include <SoftwareSerial.h>
#define SERIAL_BAUD_RATE
#define LED_PIN 13 // Our LED is on pin 13 (default on Arduino nano, uno etc)
SoftwareSerial HMISerial(10,11); // connects to the serial of the HMI, TX, RX.
bool HMIcommand = false;
byte HMIbuffer[10]; // buffer for bytes coming from HMI.
int HMIbytes = 0;
int HMIendcount = 0;
//We wait for a specific hex string from the display to turn the LED on or off
byte switch_led_state[7] = {0x65,0x01,0x01,0x01,0xFF,0xFF,0xFF};
byte led_state =0; // Zero is off, so that’s how we start.
void setup()
{
Serial.begin(SERIAL_BAUD_RATE);
HMISerial.begin(SERIAL_BAUD_RATE);
}
void loop()
{
if (HMISerial.available())
{
byte inbyte;
inbyte = HMISerial.read();
//Serial.println(inbyte); print inbyte for debug purposes
if (inbyte == 0xFF)
{
HMIendcount++;
if (HMIendcount == 3)
{
HMIcommand = true;
HMIendcount = 0;
}
}
else
{
HMIendcount = 0;
}
HMIbuffer[HMIbytes] = inbyte;
HMIbytes++;
//Serial.println(“Loop ran again”); for debugging
}
// Process HMI data when they have been completely received and print them to the Serial interface
if(HMIcommand==true)
{
// For debugging purposes, shows what HMIbytes includes and the hex string from the HMI
/*
Serial.println(HMIbytes);
for (int x = 0; x < HMIbytes; x++)
{
Serial.print(HMIbuffer[x],HEX); // For debugging purposes
if(HMIbuffer[x] == 0x65)
Serial.print(” “);
}
Serial.println(“”);
*/
// compare arrays and do stuff – Here we check for the right hex value that changes the state of the LED.
int allMatch = 1;
for (int i = 0; i < HMIbytes; i++)
{
if (HMIbuffer[i] != switch_led_state[i])
{
allMatch = 0;
break; //If there’s a mismatch we just break out of the loop.
}
}
if(allMatch == 1)
{
Serial.println(“\nSwitch LED state\n”);
if(led_state < 1)
{
digitalWrite(LED_PIN,HIGH);
led_state = 1; // We set the led_state to 1 so the program is aware that the LED is turned on.
}
else
digitalWrite(LED_PIN,LOW);
led_state = 0; // We set it to 0 so we know it’s off.
}
So that program should turn on and off the LED depending on the LED’s state and whether we are pushing the button or not. Let us break that down a bit to understand what we are doing. Most of these things should be pretty much explain them selves, so let us look at the interesting bits.
The HMI display ends all it’s hex strings with 0xFF 0xFF 0xFF. So whenever we see those three sisters, we know that the display has stopped sending us something and whatever comes next is a new event.
bool HMIcommand = false;
byte HMIbuffer[10]; // buffer for bytes coming from HMI.
int HMIbytes = 0;
int HMIendcount = 0;
HMIcommand is basically used to let us know whether we’ve received those three 0xFFs or not. HMIbuffer contains the string from the display and HMIbytes would be each byte that the display sends. Note that 0xFF is one byte, or 1111 1111 in binary, or 255 in decimal, so it fits nicely in our byte array of 7 bytes. Ie. each row in that array is a byte in size.
//We wait for a specific hex string from the display to turn the LED on or off
byte switch_led_state[7] = {0x65,0x01,0x01,0x01,0xFF,0xFF,0xFF};
byte led_state =0; // Zero is off, so that’s how we start.
The string of hex values we’re looking for is 0x65,0x01,0x01,0x01,0xFF,0xFF,0xFF. 0x65 is a touch event. First 0x01 is the page it’s happening at on the HMI. Second 0x01 is the ID of the button that is being pressed. The third is a touch event, we’ll not dig into that now. And then come the three 0xFF.
void setup()
{
Serial.begin(SERIAL_BAUD_RATE);
HMISerial.begin(SERIAL_BAUD_RATE);
}
We start both of the Serial ports, former for debugging, latter for the HMI serial port. Note that the display runs at 9600baud by default.
void loop()
{
if (HMISerial.available())
{
byte inbyte;
inbyte = HMISerial.read();
//Serial.println(inbyte); print inbyte for debug purposes
if (inbyte == 0xFF)
{
HMIendcount++;
if (HMIendcount == 3)
{
HMIcommand = true;
HMIendcount = 0;
}
}
else
{
HMIendcount = 0;
}
HMIbuffer[HMIbytes] = inbyte;
HMIbytes++;
//Serial.println(“Loop ran again”); for debugging
}
If the serial is available, we read what’s coming from it, and put that into the array HMIbuffer.
// Process HMI data when they have been completely received and print them to the Serial interface
if(HMIcommand==true)
{
Here comes the HMIcommand == true, if the first loop filled up it’s buffer, defined by the three 0xFFs, we look into what data the array holds.
// For debugging purposes, shows what HMIbytes includes and the hex string from the HMI
/*
Serial.println(HMIbytes);
for (int x = 0; x < HMIbytes; x++)
{
Serial.print(HMIbuffer[x],HEX); // For debugging purposes
if(HMIbuffer[x] == 0x65)
Serial.print(” “);
}
Serial.println(“”);
*/
The debugging function prints out the received hex string.
// compare arrays and do stuff – Here we check for the right hex value that changes the state of the LED.
int allMatch = 1;
for (int i = 0; i < HMIbytes; i++)
{
if (HMIbuffer[i] != switch_led_state[i])
{
allMatch = 0;
break; //If there’s a mismatch we just break out of the loop.
}
}
Here we look at the HMIbuffer array and compare it to our own predefined array that contains only one matching hex string, which in turn changes the state of the LED.
if(allMatch == 1)
If it matches, we print a message to the debug serial consol, and check the state of the LED.
{
Serial.println(“\nSwitch LED state\n”);
if(led_state < 1)
If the led_state variable is less than zero, we assume it’s 1 or higher, and turn on the LED.
{
digitalWrite(LED_PIN,HIGH);
led_state = 1; // We set the led_state to 1 so the program is aware that the LED is turned on.
}
Otherwise, it’s not turned off and we turn it off.
else
digitalWrite(LED_PIN,LOW);
led_state = 0; // We set it to 0 so we know it’s off.
}
So that’s it! A bit of code to play against the Nextion HMI display to get you started. Of course there are libraries from Nextion that makes it easier, but my experience with them has been rather bad, so I ended up with just playing with the raw hex values rather than the libraries.