Firmware auf dem Nextion HMI unter Linux hochladen
Published by dg5kr on Donnerstag, Juli 23, 2020
Das Nextion HMI (Human Machine Interface) ist ein Touchdisplay der besonderen Art. In diesem Artikel möchte ich gar nicht so sehr auf das Display, deren Eigenschaft und Einsatz eingehen. Hier verweise ich auf die unten angegeben Artikel. Viel mehr möchte ich über das „Bestücken“ der Firmware des HMI schreiben. Nextion bietet dabei zwei Möglichkeiten an:
1. Firmware Upload via SD Karte
2. Firmware Upload via serielle Schnittstelle
Bei 1. Wird die Firmware auf eine FAT32 formatierte mini SD kopiert. Nach dem einlegen im Nextion HMI wird diese Firmware nach dem Einschalten automatisch in das HMI geladen. Allerdings ist diese Methode recht umständlich, da man immer physisch an die Platine des Display ran muss.
Punkt 2 ist wohl meist genutzte Methode. Hier das Display mittels FTDI Adapter (TTL-Serial auf USB) am Rechner angeschlossen und die Firmware kann dann mit der Nextion IDE aufgespielt werden.
Nun, hier ist die Krux. Die IDE gibt es ausschließlich für Windows. Andere native „Upload-Programme“ sind ebenfalls bisher nur unter Windows zu finden.
Nun habe ich aber das Problem das ich das Nextion-Display als „Statusanzeige“ für meinen DMR Jumbospot nutze und in einem Gehäuse fest verbaut ist. Dort läuft ein MMDV auf basis PISTAR. Schnell kam der Wunsch die Firmware von ON7LDS nach meinen Wünschen anzupassen. Leider musste ich dazu jedes Mal das Gehäuse öffnen und den Nextion Anschluss vom Raspberry Pi trennen um ich dann am PC zu verbinden und die Firmware auf das HMI zu laden – ganz schön umständlich.
Meine Recherche fanden für Linux ein Python Programm, was dies tun soll. Leider funktioniert das Programm nicht und schien auch nicht weiter gepflegt zu werden. Also entschied ich mich ein solches Programm selber zu schreiben. Ich entschied mich für C, da ich in Python nicht so bewandert bin.
Raus gekommen ist ein Tool, welches per Befehlszeile eine kompilierte (TFT-File) Firmware für das Nextion-Display unter Linux aufspielt. Im Beispiel von PI-STAR spiele ich die Firmware über das Netzwerk einfach in Home Verzeichnis vom User pi-star und rufen dann über die SSH Konsole der PI-STAR Webseite das Tool auf.
// nextion_upload.c // Send Firmwareupdates to the Nextiondisplay // // Parm1 = Port // Parm2 = TFT-File #include <ctype.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <string.h> #include <errno.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> /* ------------------------------------------------------------------------------------------------------------------ */ // Global VARS char hostname [16]; char *portname = ""; char *filename = ""; int USB; // See https://nextion.tech/2017/12/08/nextion-hmi-upload-protocol-v1-1/ //Baudrate 2400 4800 9600 19200 38400 57600 115200 //Delay(ms) 447 239 135 83 57 48 39 int TICKWAIT = 135000; int BAUDCOMM = 9600; int BAUDUPLOAD = 115200; char ACKCODE=0x05; char ENDCODE=0x88; /* ------------------------------------------------------------------------------------------------------------------ */ void CLS() { // CLS printf("33[3;J33[H33[2J"); } /* ------------------------------------------------------------------------------------------------------------------ */ void print_usage() { // CLS CLS(); // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 printf("\nNEXTION_UPLOAD, sendet neue Firmware zum Nextiondisplay\n"); printf("(C) 2020 by DG5KR\n\n"); printf("Aufruf: nextion_upload /dev/ttyUSB[n] TFT-Filenamen\n"); exit(0); } /* ------------------------------------------------------------------------------------------------------------------ */ void GOTOXY(int x,int y) { printf("%c[%d;%df",0x1B,y,x); } /* ------------------------------------------------------------------------------------------------------------------ */ void intHandler(int dummy) { int ANT; printf("Programm wirklich abbrechen (j/n) "); ANT=getchar(); if(ANT==106) { // start MMDVM // gethostname(hostname,16); if(strcmp(hostname,"hotspot")==0) { system("/usr/bin/sudo /bin/systemctl start mmdvmhost.timer"); system("/usr/bin/sudo /bin/systemctl start mmdvmhost"); usleep(1000000); } exit(127); } } /* ------------------------------------------------------------------------------------------------------------------ */ int get_file_size() { struct stat st; stat(filename, &st); int size = st.st_size; return size; } /* ------------------------------------------------------------------------------------------------------------------ */ int openport_init(char *portname) { USB = open( portname, O_RDWR| O_NOCTTY | O_NDELAY ); // Init Port struct termios tty; memset (&tty, 0, sizeof tty); // Set Baud Rate cfsetospeed (&tty, (speed_t)B9600); cfsetispeed (&tty, (speed_t)B9600); // Setting other Port Stuff tty.c_cflag &= ~PARENB; // Make 8n1 tty.c_cflag &= ~CSTOPB; tty.c_cflag &= ~CSIZE; tty.c_cflag |= CS8; tty.c_cflag &= ~CRTSCTS; // no flow control tty.c_cc[VMIN] = 10; // read doesn't block tty.c_cc[VTIME] = 0; // no read timeout tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines tty.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Flush Port, then applies attributes tcflush( USB, TCIFLUSH ); if ( tcsetattr ( USB, TCSANOW, &tty ) != 0) { printf("Error flush serial: %s", strerror(errno)); printf("nn"); exit(errno); } } int openport_upload(char *portname) { USB = open( portname, O_RDWR| O_NOCTTY | O_NDELAY ); // Init Port struct termios tty; memset (&tty, 0, sizeof tty); // Set Baud Rate cfsetospeed (&tty, (speed_t)B115200); cfsetispeed (&tty, (speed_t)B115200); // Setting other Port Stuff tty.c_cflag &= ~PARENB; // Make 8n1 tty.c_cflag &= ~CSTOPB; tty.c_cflag &= ~CSIZE; tty.c_cflag |= CS8; tty.c_cflag &= ~CRTSCTS; // no flow control tty.c_cc[VMIN] = 10; // read doesn't block tty.c_cc[VTIME] = 0; // no read timeout tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines tty.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Flush Port, then applies attributes tcflush( USB, TCIFLUSH ); if ( tcsetattr ( USB, TCSANOW, &tty ) != 0) { printf("Error flush serial: %s", strerror(errno)); printf("nn"); exit(errno); } } /* ------------------------------------------------------------------------------------------------------------------ */ int send_cmd(char *cmd_str) { int n = write(USB,cmd_str,strlen(cmd_str)); n = n + write(USB,"xFF",1); n = n + write(USB,"xFF",1); n = n + write(USB,"xFF",1); usleep(TICKWAIT); return n; } /* ------------------------------------------------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------------------------------------------------ */ int main(int argc, char *argv[]) { /* ------------------------------------------------------------------------------------------------------------------ */ // trap CTRL-C signal(SIGINT, intHandler); /* ------------------------------------------------------------------------------------------------------------------ */ int n=0; /* ------------------------------------------------------------------------------------------------------------------ */ // Es wurde kein Paramter uebergeben if(argc < 2) { print_usage(); } portname = argv[1]; filename = argv[2]; /* ------------------------------------------------------------------------------------------------------------------ */ // Hole Hostname gethostname(hostname,16); /* ------------------------------------------------------------------------------------------------------------------ */ // stop MMDVM if(strcmp(hostname,"hotspot")==0) { system("/usr/bin/sudo /bin/systemctl stop mmdvmhost"); system("/usr/bin/sudo /bin/systemctl stop mmdvmhost.timer"); usleep(1000000); } /* ------------------------------------------------------------------------------------------------------------------ */ // Open Port openport_init(argv[1]); // Ready zum Daten Empfang n = send_cmd(""); n = send_cmd("connect"); printf("Bytes Txed %d, ", n); // Lese Ergebnis char read_buffer[1024]; int bytes_read = 0; int i = 0; bytes_read = read(USB,&read_buffer,256); printf("Bytes Rxed %dn", bytes_read); printf("ACKN: %sn", read_buffer); // Filelänge ? int flen=get_file_size(); printf("Bytes to send of %s: %d bytesn", filename,flen); // Befehl um in den Upload mod zu gehen // whmi-wri filesize,baud,res0ÿÿÿ char s[80] = ""; sprintf(s,"whmi-wri %d,%d,res0",flen,BAUDUPLOAD); send_cmd(s); // Warten auf ACK char BUF[1]; bytes_read = read(USB,BUF,1); while (bytes_read <= 0) { bytes_read = read(USB,BUF,1); usleep(TICKWAIT); } // Setzen der Upload Rate close(USB); openport_upload(argv[1]); usleep(150000); // Jetzt schieben wir 4096Byte Packete dahin int fd = open(filename, O_RDONLY); char buffer[flen]; int pack_len=4096; for (i=0 ; i < flen ; i=i+pack_len) { // process bytes at buffer[i] read(fd, buffer, pack_len); write(USB,buffer,pack_len); GOTOXY(0,5); printf("Sending firmware to HMI: Block %8d of %d",i,flen); GOTOXY(0,6); usleep(TICKWAIT); // Warten auf ACK char BUF[1]; bytes_read = read(USB,BUF,1); while (bytes_read <= 0) { bytes_read = read(USB,BUF,1); usleep(TICKWAIT); } usleep(TICKWAIT); } // Und den Rest pack_len=flen-i; read(fd, buffer, pack_len); write(USB,buffer,pack_len); GOTOXY(0,5); printf("Sending firmware to HMI: Block %8d of %d",i,flen); GOTOXY(0,6); close(fd); close(USB); /* ------------------------------------------------------------------------------------------------------------------ */ // start MMDVM if(strcmp(hostname,"hotspot")==0) { system("/usr/bin/sudo /bin/systemctl start mmdvmhost.timer"); system("/usr/bin/sudo /bin/systemctl start mmdvmhost"); } /* ------------------------------------------------------------------------------------------------------------------ */ exit(0); } Programm compilieren mit: gcc nextion_upload.c -o nextionupload -I/usr/local/include -L/usr/local/lib -l ncurses
Informationen und Projekte rund um das Nextion Display:
Nextion
Firmware Upload Protocol
ON7LDS – Nextion Displays
ON7LDS – Nextion Displays
Ein Nextion Serial Client in Python