2023-11-15 21:55:05 +03:00
/**
* @ file NexUpload . cpp
*
* The implementation of uploading tft file for nextion displays .
*
* Original version ( a part of https : //github.com/itead/ITEADLIB_Arduino_Nextion)
* @ author Chen Zengpeng ( email : < zengpeng . chen @ itead . cc > )
* @ date 2016 / 3 / 29
* @ copyright
* Copyright ( C ) 2014 - 2015 ITEAD Intelligent Systems Co . , Ltd .
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , version 3.
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
*/
# define DEBUG_SERIAL_ENABLE
# include "ESPNexUpload.h"
# ifdef DEBUG_SERIAL_ENABLE
# define dbSerialPrint(a) Serial.print(a)
# define dbSerialPrintHex(a) Serial.print(a, HEX)
# define dbSerialPrintln(a) Serial.println(a)
# define dbSerialBegin(a) Serial.begin(a)
# else
# define dbSerialPrint(a) \
do \
{ \
} while ( 0 )
# define dbSerialPrintHex(a) \
do \
{ \
} while ( 0 )
# define dbSerialPrintln(a) \
do \
{ \
} while ( 0 )
# define dbSerialBegin(a) \
do \
{ \
} while ( 0 )
# endif
2024-05-15 22:25:23 +03:00
ESPNexUpload : : ESPNexUpload ( uint32_t upload_baudrate , int line , int rx , int tx )
2023-11-15 21:55:05 +03:00
{
_upload_baudrate = upload_baudrate ;
2024-05-15 22:25:23 +03:00
_rx = rx ;
_tx = tx ;
_line = line ;
2023-11-15 21:55:05 +03:00
# if defined ESP8266
2024-05-15 22:25:23 +03:00
nexSerial = new SoftwareSerial ( _rx , _tx ) ;
# else
if ( line > = 0 ) {
nexSerial = new HardwareSerial ( line ) ;
// ((HardwareSerial*)nexSerial)->begin(_upload_baudrate, SERIAL_8N1, _rx, _tx);
} else {
nexSerial = new SoftwareSerial ( _rx , _tx ) ;
// ((SoftwareSerial*)nexSerial)->begin(_upload_baudrate);
}
2023-11-15 21:55:05 +03:00
# endif
}
2024-05-15 22:25:23 +03:00
void ESPNexUpload : : nexSerialBegin ( uint32_t _speed , int _line , int _rx , int _tx )
{
# if defined ESP8266
nexSerial - > begin ( _speed ) ;
# else
if ( _line > = 0 ) {
( ( HardwareSerial * ) nexSerial ) - > begin ( _speed , SERIAL_8N1 , _rx , _tx ) ;
} else {
( ( SoftwareSerial * ) nexSerial ) - > begin ( _speed ) ;
}
# endif
}
2023-11-15 21:55:05 +03:00
bool ESPNexUpload : : connect ( )
{
# if defined ESP8266
yield ( ) ;
# endif
dbSerialBegin ( 115200 ) ;
_printInfoLine ( F ( " serial tests & connect " ) ) ;
if ( _getBaudrate ( ) = = 0 )
{
statusMessage = F ( " get baudrate error " ) ;
_printInfoLine ( statusMessage ) ;
return false ;
}
_setRunningMode ( ) ;
if ( ! _echoTest ( " mystop_yesABC " ) )
{
statusMessage = F ( " echo test failed " ) ;
_printInfoLine ( statusMessage ) ;
return false ;
}
if ( ! _handlingSleepAndDim ( ) )
{
statusMessage = F ( " handling sleep and dim settings failed " ) ;
_printInfoLine ( statusMessage ) ;
return false ;
}
if ( ! _setPrepareForFirmwareUpdate ( _upload_baudrate ) )
{
statusMessage = F ( " modifybaudrate error " ) ;
_printInfoLine ( statusMessage ) ;
return false ;
}
return true ;
}
bool ESPNexUpload : : prepareUpload ( uint32_t file_size )
{
_undownloadByte = file_size ;
return this - > connect ( ) ;
}
uint16_t ESPNexUpload : : _getBaudrate ( void )
{
_baudrate = 0 ;
uint32_t baudrate_array [ 7 ] = { 115200 , 19200 , 9600 , 57600 , 38400 , 4800 , 2400 } ;
for ( uint8_t i = 0 ; i < 7 ; i + + )
{
if ( _searchBaudrate ( baudrate_array [ i ] ) )
{
_baudrate = baudrate_array [ i ] ;
_printInfoLine ( F ( " baudrate determined " ) ) ;
break ;
}
delay ( 1500 ) ; // wait for 1500 ms
}
return _baudrate ;
}
bool ESPNexUpload : : _searchBaudrate ( uint32_t baudrate )
{
# if defined ESP8266
yield ( ) ;
# endif
String response = String ( " " ) ;
_printInfoLine ( ) ;
dbSerialPrint ( F ( " init nextion serial interface on baudrate: " ) ) ;
dbSerialPrintln ( baudrate ) ;
2024-05-15 22:25:23 +03:00
nexSerialBegin ( baudrate , _line , _rx , _tx ) ;
2023-11-15 21:55:05 +03:00
_printInfoLine ( F ( " ESP baudrate established, try to connect to display " ) ) ;
const char _nextion_FF_FF [ 3 ] = { 0xFF , 0xFF , 0x00 } ;
this - > sendCommand ( " DRAKJHSUYDGBNCJHGJKSHBDN " ) ;
this - > sendCommand ( " " , true , true ) ; // 0x00 0xFF 0xFF 0xFF
this - > recvRetString ( response ) ;
if ( response [ 0 ] ! = 0x1A )
{
_printInfoLine ( F ( " first indication that baudrate is wrong " ) ) ;
}
else
{
_printInfoLine ( F ( " first respone from display, first indication that baudrate is correct " ) ) ;
}
this - > sendCommand ( " connect " ) ; // first connect attempt
this - > recvRetString ( response ) ;
if ( response . indexOf ( F ( " comok " ) ) = = - 1 )
{
_printInfoLine ( F ( " display doesn't accept the first connect request " ) ) ;
}
else
{
_printInfoLine ( F ( " display accept the first connect request " ) ) ;
}
response = String ( " " ) ;
delay ( 110 ) ; // based on serial analyser from Nextion editor V0.58 to Nextion display NX4024T032_011R
this - > sendCommand ( _nextion_FF_FF , false ) ;
this - > sendCommand ( " connect " ) ; // second attempt
this - > recvRetString ( response ) ;
if ( response . indexOf ( F ( " comok " ) ) = = - 1 & & response [ 0 ] ! = 0x1A )
{
_printInfoLine ( F ( " display doesn't accept the second connect request " ) ) ;
_printInfoLine ( F ( " conclusion, wrong baudrate " ) ) ;
return 0 ;
}
else
{
_printInfoLine ( F ( " display accept the second connect request " ) ) ;
_printInfoLine ( F ( " conclusion, correct baudrate " ) ) ;
}
return 1 ;
}
void ESPNexUpload : : sendCommand ( const char * cmd , bool tail , bool null_head )
{
# if defined ESP8266
yield ( ) ;
# endif
if ( null_head )
{
2024-05-15 22:25:23 +03:00
( ( HardwareSerial * ) nexSerial ) - > write ( 0x00 ) ;
2023-11-15 21:55:05 +03:00
}
2024-05-15 22:25:23 +03:00
while ( nexSerial - > available ( ) )
2023-11-15 21:55:05 +03:00
{
2024-05-15 22:25:23 +03:00
nexSerial - > read ( ) ;
2023-11-15 21:55:05 +03:00
}
2024-05-15 22:25:23 +03:00
nexSerial - > print ( cmd ) ;
2023-11-15 21:55:05 +03:00
if ( tail )
{
2024-05-15 22:25:23 +03:00
nexSerial - > write ( 0xFF ) ;
nexSerial - > write ( 0xFF ) ;
nexSerial - > write ( 0xFF ) ;
2023-11-15 21:55:05 +03:00
}
_printSerialData ( true , cmd ) ;
}
uint16_t ESPNexUpload : : recvRetString ( String & response , uint32_t timeout , bool recv_flag )
{
# if defined ESP8266
yield ( ) ;
# endif
uint16_t ret = 0 ;
uint8_t c = 0 ;
uint8_t nr_of_FF_bytes = 0 ;
long start ;
bool exit_flag = false ;
bool ff_flag = false ;
if ( timeout ! = 500 )
_printInfoLine ( " timeout setting serial read: " + String ( timeout ) ) ;
start = millis ( ) ;
while ( millis ( ) - start < = timeout )
{
2024-05-15 22:25:23 +03:00
while ( nexSerial - > available ( ) )
2023-11-15 21:55:05 +03:00
{
2024-05-15 22:25:23 +03:00
c = nexSerial - > read ( ) ;
2023-11-15 21:55:05 +03:00
if ( c = = 0 )
{
continue ;
}
if ( c = = 0xFF )
nr_of_FF_bytes + + ;
else
{
nr_of_FF_bytes = 0 ;
ff_flag = false ;
}
if ( nr_of_FF_bytes > = 3 )
ff_flag = true ;
response + = ( char ) c ;
if ( recv_flag )
{
if ( response . indexOf ( 0x05 ) ! = - 1 )
{
exit_flag = true ;
}
}
}
if ( exit_flag | | ff_flag )
{
break ;
}
}
_printSerialData ( false , response ) ;
// if the exit flag and the ff flag are both not found, than there is a timeout
// if(!exit_flag && !ff_flag)
// _printInfoLine(F("recvRetString: timeout"));
if ( ff_flag )
response = response . substring ( 0 , response . length ( ) - 3 ) ; // Remove last 3 0xFF
ret = response . length ( ) ;
return ret ;
}
bool ESPNexUpload : : _setPrepareForFirmwareUpdate ( uint32_t upload_baudrate )
{
# if defined ESP8266
yield ( ) ;
# endif
String response = String ( " " ) ;
String cmd = String ( " " ) ;
cmd = F ( " 00 " ) ;
this - > sendCommand ( cmd . c_str ( ) ) ;
delay ( 0.1 ) ;
this - > recvRetString ( response , 800 , true ) ; // normal response time is 400ms
String filesize_str = String ( _undownloadByte , 10 ) ;
String baudrate_str = String ( upload_baudrate ) ;
cmd = " whmi-wri " + filesize_str + " , " + baudrate_str + " ,0 " ;
this - > sendCommand ( cmd . c_str ( ) ) ;
// Without flush, the whmi command will NOT transmitted by the ESP in the current baudrate
// because switching to another baudrate (nexSerialBegin command) has an higher prio.
// The ESP will first jump to the new 'upload_baudrate' and than process the serial 'transmit buffer'
// The flush command forced the ESP to wait until the 'transmit buffer' is empty
2024-05-15 22:25:23 +03:00
nexSerial - > flush ( ) ;
2023-11-15 21:55:05 +03:00
2024-05-15 22:25:23 +03:00
nexSerialBegin ( upload_baudrate , _line , _rx , _tx ) ;
2023-11-15 21:55:05 +03:00
_printInfoLine ( F ( " changing upload baudrate... " ) ) ;
_printInfoLine ( String ( upload_baudrate ) ) ;
this - > recvRetString ( response , 800 , true ) ; // normal response time is 400ms
// The Nextion display will, if it's ready to accept data, send a 0x05 byte.
if ( response . indexOf ( 0x05 ) ! = - 1 )
{
_printInfoLine ( F ( " preparation for firmware update done " ) ) ;
return 1 ;
}
else
{
_printInfoLine ( F ( " preparation for firmware update failed " ) ) ;
return 0 ;
}
}
void ESPNexUpload : : setUpdateProgressCallback ( THandlerFunction value )
{
_updateProgressCallback = value ;
}
bool ESPNexUpload : : upload ( const uint8_t * file_buf , size_t buf_size )
{
# if defined ESP8266
yield ( ) ;
# endif
uint8_t c ;
uint8_t timeout = 0 ;
String string = String ( " " ) ;
for ( uint16_t i = 0 ; i < buf_size ; i + + )
{
// Users must split the .tft file contents into 4096 byte sized packets with the final partial packet size equal to the last remaining bytes (<4096 bytes).
if ( _sent_packets = = 4096 )
{
// wait for the Nextion to return its 0x05 byte confirming reception and readiness to receive the next packets
this - > recvRetString ( string , 500 , true ) ;
if ( string . indexOf ( 0x05 ) ! = - 1 )
{
// reset sent packets counter
_sent_packets = 0 ;
// reset receive String
string = " " ;
}
else
{
if ( timeout > = 8 )
{
statusMessage = F ( " serial connection lost " ) ;
_printInfoLine ( statusMessage ) ;
return false ;
}
timeout + + ;
}
// delay current byte
i - - ;
}
else
{
// read buffer
c = file_buf [ i ] ;
// write byte to nextion over serial
2024-05-15 22:25:23 +03:00
nexSerial - > write ( c ) ;
2023-11-15 21:55:05 +03:00
// update sent packets counter
_sent_packets + + ;
}
}
return true ;
}
bool ESPNexUpload : : upload ( Stream & myFile )
{
# if defined ESP8266
yield ( ) ;
# endif
// create buffer for read
2024-05-15 22:25:23 +03:00
uint8_t buff [ 4096 ] = { 0 } ;
2023-11-15 21:55:05 +03:00
// read all data from server
while ( _undownloadByte > 0 | | _undownloadByte = = - 1 )
{
// get available data size
size_t size = myFile . available ( ) ;
if ( size )
{
// read up to 2048 byte into the buffer
int c = myFile . readBytes ( buff , ( ( size > sizeof ( buff ) ) ? sizeof ( buff ) : size ) ) ;
// Write the buffered bytes to the nextion. If this fails, return false.
if ( ! this - > upload ( buff , c ) )
{
return false ;
}
else
{
if ( _updateProgressCallback )
{
_updateProgressCallback ( ) ;
}
}
if ( _undownloadByte > 0 )
{
_undownloadByte - = c ;
}
}
delay ( 1 ) ;
}
return true ;
}
void ESPNexUpload : : softReset ( void )
{
// soft reset nextion device
this - > sendCommand ( " rest " ) ;
}
void ESPNexUpload : : end ( )
{
// wait for the nextion to finish internal processes
delay ( 1600 ) ;
// soft reset the nextion
this - > softReset ( ) ;
// end Serial connection
2024-05-15 22:25:23 +03:00
( ( HardwareSerial * ) nexSerial ) - > end ( ) ;
2023-11-15 21:55:05 +03:00
// reset sent packets counter
_sent_packets = 0 ;
statusMessage = F ( " upload ok " ) ;
_printInfoLine ( statusMessage + F ( " \r \n " ) ) ;
}
void ESPNexUpload : : _setRunningMode ( void )
{
String cmd = String ( " " ) ;
delay ( 100 ) ;
cmd = F ( " runmod=2 " ) ;
this - > sendCommand ( cmd . c_str ( ) ) ;
delay ( 60 ) ;
}
bool ESPNexUpload : : _echoTest ( String input )
{
String cmd = String ( " " ) ;
String response = String ( " " ) ;
cmd = " print \" " + input + " \" " ;
this - > sendCommand ( cmd . c_str ( ) ) ;
uint32_t duration_ms = calculateTransmissionTimeMs ( cmd ) * 2 + 10 ; // times 2 (send + receive) and 10 ms extra
this - > recvRetString ( response , duration_ms ) ;
return ( response . indexOf ( input ) ! = - 1 ) ;
}
bool ESPNexUpload : : _handlingSleepAndDim ( void )
{
String cmd = String ( " " ) ;
String response = String ( " " ) ;
bool set_sleep = false ;
bool set_dim = false ;
cmd = F ( " get sleep " ) ;
this - > sendCommand ( cmd . c_str ( ) ) ;
this - > recvRetString ( response ) ;
if ( response [ 0 ] ! = 0x71 )
{
statusMessage = F ( " unknown response from 'get sleep' request " ) ;
_printInfoLine ( statusMessage ) ;
return false ;
}
if ( response [ 1 ] ! = 0x00 )
{
_printInfoLine ( F ( " sleep enabled " ) ) ;
set_sleep = true ;
}
else
{
_printInfoLine ( F ( " sleep disabled " ) ) ;
}
response = String ( " " ) ;
cmd = F ( " get dim " ) ;
this - > sendCommand ( cmd . c_str ( ) ) ;
this - > recvRetString ( response ) ;
if ( response [ 0 ] ! = 0x71 )
{
statusMessage = F ( " unknown response from 'get dim' request " ) ;
_printInfoLine ( statusMessage ) ;
return false ;
}
if ( response [ 1 ] = = 0x00 )
{
_printInfoLine ( F ( " dim is 0%, backlight from display is turned off " ) ) ;
set_dim = true ;
}
else
{
_printInfoLine ( ) ;
dbSerialPrint ( F ( " dim " ) ) ;
dbSerialPrint ( ( uint8_t ) response [ 1 ] ) ;
dbSerialPrintln ( F ( " % " ) ) ;
}
if ( ! _echoTest ( " ABC " ) )
{
statusMessage = F ( " echo test in 'handling sleep and dim' failed " ) ;
_printInfoLine ( statusMessage ) ;
return false ;
}
if ( set_sleep )
{
cmd = F ( " sleep=0 " ) ;
this - > sendCommand ( cmd . c_str ( ) ) ;
// Unfortunately the display doesn't send any respone on the wake up request (sleep=0)
// Let the ESP wait for one second, this is based on serial analyser from Nextion editor V0.58 to Nextion display NX4024T032_011R
// This gives the Nextion display some time to wake up
delay ( 1000 ) ;
}
if ( set_dim )
{
cmd = F ( " dim=100 " ) ;
this - > sendCommand ( cmd . c_str ( ) ) ;
delay ( 15 ) ;
}
return true ;
}
void ESPNexUpload : : _printSerialData ( bool esp_request , String input )
{
char c ;
if ( esp_request )
dbSerialPrint ( F ( " ESP request: " ) ) ;
else
dbSerialPrint ( F ( " Nextion respone: " ) ) ;
if ( input . length ( ) = = 0 )
{
dbSerialPrintln ( F ( " none " ) ) ;
return ;
}
for ( int i = 0 ; i < input . length ( ) ; i + + )
{
c = input [ i ] ;
if ( ( uint8_t ) c > = 0x20 & & ( uint8_t ) c < = 0x7E )
dbSerialPrint ( c ) ;
else
{
dbSerialPrint ( F ( " 0x " ) ) ;
dbSerialPrintHex ( c ) ;
dbSerialPrint ( F ( " " ) ) ;
}
}
dbSerialPrintln ( ) ;
}
uint32_t ESPNexUpload : : calculateTransmissionTimeMs ( String message )
{
// In general, 1 second (s) = 1000 (10^-3) millisecond (ms) or
// 1 second (s) = 1000 000 (10^-6) microsecond (us).
// To calculate how much microsecond one BIT of data takes with a certain baudrate you have to divide
// the baudrate by one second.
// For example 9600 baud = 1000 000 us / 9600 ≈ 104 us
// The time to transmit one DATA byte (if we use default UART modulation) takes 10 bits.
// 8 DATA bits and one START and one STOP bit makes 10 bits.
// In this example (9600 baud) a byte will take 1041 us to send or receive.
// Multiply this value by the length of the message (number of bytes) and the total transmit/ receive time
// is calculated.
uint32_t duration_one_byte_us = 10000000 / _baudrate ; // 1000 000 * 10 bits / baudrate
uint16_t nr_of_bytes = message . length ( ) + 3 ; // 3 times 0xFF byte
uint32_t duration_message_us = nr_of_bytes * duration_one_byte_us ;
uint32_t return_value_ms = duration_message_us / 1000 ;
_printInfoLine ( " calculated transmission time: " + String ( return_value_ms ) + " ms " ) ;
return return_value_ms ;
}
void ESPNexUpload : : _printInfoLine ( String line )
{
dbSerialPrint ( F ( " Status info: " ) ) ;
if ( line . length ( ) ! = 0 )
dbSerialPrintln ( line ) ;
}