Motivation
Das zugrunde liegende Projekt ist ein Internetradio mit Benzinpreisanzeige (Tankerkönig), das zwei Etagen tiefer in der Küche installiert werden wird. Basis sind ein ESP32 Dev Kit C, ein VS1053 MP3-Decoder, ein KY-040, ein analoger Verstärker und ein Nextion NX4832T035 (nicht verfügbar, äquivalent). Evtl erforderliche Updates sollen vom "Entwicklungsstandort" aus durchgeführt werden können - wer weiß, was mir noch an Flausen einfallen wird (Wetter?) oder ob die Hauptnutzerin mit den Farben hadert.
Informationsquellen
Edzelfs ESP32 Radio war initialer Aufsetzpunkt; aber ich habe mich beim Anpassen des Codes auf meine Anforderungen so verheddert, dass ich es neu gemacht habe. Der Display-Update-Code findet sich dort im Hauptprogramm ab Zeile 2217; allerdings wird dort das TFT file über den Ota-Client von seinem Server bezogen.
Zweite Quelle ist Nextion selbst, da ist das Update-Protokoll v1.1 beschrieben. Da ich weder "Addressing" noch "Reparse"-Mode einsetze, reicht eine einfache Implementierung auch aus.
Ablauf
Das Display ist bei mir an Serial2 (Pin 16 RxD und 17 TxD) angeschlossen, die zu übertragende Datei liegt im Filesystem (LITTLEFS) vor (Bibliotheksverwaltung LittleFS_esp32); zum Upload nehme ich die passende Erweiterung der Arduino-IDE.
- Öffne die Datei und hole dabei deren Größe (brauchen wir gleich)
- Optional: Ändere Baudrate für die Übertragung zum Display
Serial2.printf("baud=57600\xFF\xFF\xFF");
Serial2.begin(57600);
- Sende ein Leerkommando
Serial2.printf("\xFF\xFF\xFF");
- Erwarte eine Antwort vom Display, aber ignoriere den Inhalt
- Starte den Upload mit
whmi-wri <file size>,<upload speed>,0
wie bei Nextion beschrieben
Serial2.printf("whmi-wri %zu,%zu,0", updateSize, uploadSpeed);
- Warte auf Antwort 0x05 vom Display
loop:
- Lese 4096 Byte aus der Datei
- Sende 4096 Byte direkt, binär ans Display:
Serial2.write(buffer, 4096);
- Warte auf Antwort 0x05 vom Display
end loop
- Übertrage den Rest (falls vorhanden):
Serial2.write(buffer, fileSize % 4096)
- Warte auf Antwort 0x05 vom Display
- Schließe Datei
- Optional: Setze Baudrate zurück auf den Betriebswert (9600)
- Reset des ESP32
Update-Funktion (aus dem Beispiel)
Falls jemand sich das komplette Teil nicht antun will: Hier ist die unansehliche Update-Funktion. Sie braucht die unten erwähnten Methoden zur Bereitstellung der Daten aus der TFT-Datei.
bool nextionUpdate(const char* nextionTftFile)
{
TRACE();
size_t updateSize = storage.openUpdateFile(nextionTftFile);
uint8_t buffer[segmentSize];
size_t segments;
size_t rest;
size_t bytesRead = 0;
size_t bytesSent = 0;
uint8_t ch;
if (updateSize == 0)
{
DEB_PL("NEXTION UPDATE: cannot update due to missing TFT file");
return false;
}
// stop Nextion receiver task
screen.off();
delay(100);
DEB_PL("NEXTION UPDATE: system is off");
// change baud rate
DEB_PF("NEXTION UPDATE: change baud rate to %zubps\n", uploadSpeed);
Serial2.printf("baud=%zu", uploadSpeed);
screen.endCommand();
delay(500); // allow display a moment to perform the data rate switch
Serial2.begin(uploadSpeed);
delay(100);
// TODO: avoid OTA upload while loading the Nextion display
segments = updateSize / segmentSize;
rest = updateSize % segmentSize;
DEB_PF("NEXTION UPDATE: update size is %zu bytes (%zu blocks / %zu rest)\n", updateSize, segments, rest);
DEB_P("NEXTION UPDATE: send empty command ");
screen.endCommand();
// wait for reply
for (int i = 0; i < 100; i++)
{
if (Serial2.available())
{
ch = Serial2.read();
if (ch != 0xFF)
{
DEB_P(ch);
}
}
delay (20);
}
DEB_PL(" ...done");
// DO DOWNLOAD
// Start upload
DEB_PF("NEXTION UPDATE: START upload: whmi-wri %zu,%zu,0\n", updateSize, uploadSpeed);
Serial2.printf("whmi-wri %zu,%zu,0", updateSize, uploadSpeed);
screen.endCommand();
while (!Serial2.available())
{
delay(20) ;
}
ch = Serial2.read();
if (ch != 0x05)
{
DEB_PL("NEXTION UPDATE: start failed");
goto errorEnd;
}
// upload to display in chunks of segmentSize
for (size_t count = 0; count < segments; count++)
{
bytesRead = storage.readUpdateFile(segmentSize, buffer);
if (bytesRead == segmentSize)
{
// transmit to display
Serial2.write(buffer, segmentSize) ;
// wait for and read ACK
while (!Serial2.available())
{
delay(20) ;
}
ch = (char)Serial2.read();
if (ch != 0x05)
{
goto errorEnd;
}
// show progress
bytesSent += segmentSize;
DEB_PF(" %7zu (%3zu%%) sent\r", bytesSent, bytesSent * 100 / updateSize);
}
else
{
DEB_PF("\nNEXTION UPDATE: read error: wanted %zu, got %zu at segment %zu\n", segmentSize, bytesRead, count);
goto errorEnd;
}
}
// there might be some bytes left over to send
if (rest)
{
bytesRead = storage.readUpdateFile(rest, buffer);
if (bytesRead == rest)
{
// transmit to display
Serial2.write(buffer, rest) ;
// wait for and read ACK
while (!Serial2.available())
{
delay(20) ;
}
ch = (char)Serial2.read();
if (ch != 0x05)
{
goto errorEnd;
}
// show progress
bytesSent += rest;
DEB_PF(" %7zu (%3zu%%) sent\r", bytesSent, bytesSent * 100 / updateSize);
}
else
{
DEB_PF("\nNEXTION UPDATE: read error: wanted %zu, got %zu at rest\n", rest, bytesRead);
}
DEB_PL();
}
errorEnd:
DEB_PL();
DEB_PL("NEXTION UPDATE: update finished");
storage.closeUpdateFile();
// return to previous state
Serial2.begin(9600);
delay(1000);
return true;
}
Beispielcode
Warnung: Das beigefügte Beispiel ist eher grauslich (ist Projektcode um alles nicht relevante erleichtert).
Der nextionUpdate()
findet sich in util.ino
ab Zeile 77 und ist einfach dem Ablauf entsprechend runter gecodet. Die Storage
-Klasse stellt die drei dafür benötigten Methoden bereit.
size_t openUpdateFile(const char* nextionTftFile);
size_t readUpdateFile(size_t bytesToRead, uint8_t* buffer);
void closeUpdateFile();
Netzwerk-Daten in flashNext.ino
oben eintragen, Pinbelegung in config.h
, die Debugausgabe auf Serial1 lässt sich in trace.h
durch Auskommentieren des DEBUGGING abschalten. Der Rest ist Folklore (Beifang: Anzeige eines OTA-Uploads auf dem Display).
Sonst sind enthalten:
Zwei unterschiedliche HMI-Dateien, der Einfachheit halber mit denselben beiden Buttons zum Starten des Flash-Vorgangs und die TFTs für mein Display.
Initial würde ich einmal den harten Weg (Flashen Display über SD-Karte) und Upload von Daten und Programms über USB empfehlen; danach geht beides über OTA.
FlashNextion.zip (1,3 MB)