From 115cefc5573a5fa61f698056f4253c3819670ac7 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 24 Apr 2025 15:48:19 +0200 Subject: [PATCH] Fix XModem for SecureCRT telnet client --- CHANGELOG.md | 2 +- RELEASENOTES.md | 1 + tasmota/tasmota_support/support_command.ino | 5 +- .../tasmota_xdrv_driver/xdrv_120_xyzmodem.ino | 109 ++++++++++++++---- 4 files changed, 90 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54a66f6c9..defada08e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ All notable changes to this project will be documented in this file. ### Added - Command `JsonPP 0..7` to enable (>0) JSON Pretty Print on user interfaces and set number of indents - Command `JsonPP |backlog ;...` to enable JSON PP only once -- WebUI status line for MQTT and TLS, added `FUNC_WEB_STATUS` event +- WebUI status line for MQTT and TLS, added `FUNC_WEB_STATUS` event (#23326) ### Breaking Changed diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4c1e62743..bc00abd9d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -118,6 +118,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm ### Added - Command `JsonPP 0..7` to enable (>0) JSON Pretty Print on user interfaces and set number of indents - Command `JsonPP |backlog ;...` to enable JSON PP only once +- WebUI status line for MQTT and TLS, added `FUNC_WEB_STATUS` event [#23326](https://github.com/arendst/Tasmota/issues/23326) ### Breaking Changed diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index 5c5c37d55..352b13c15 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -912,6 +912,7 @@ void CmndStatus(void) char stemp[200]; char stemp2[TOPSZ]; + // Status if ((0 == payload) || (-99 == payload)) { uint32_t maxfn = (TasmotaGlobal.devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!TasmotaGlobal.devices_present) ? 1 : TasmotaGlobal.devices_present; #ifdef USE_SONOFF_IFAN @@ -1111,6 +1112,7 @@ void CmndStatus(void) CmndStatusResponse(6); } + // Status 7 - StatusTIM if ((0 == payload) || (7 == payload)) { if (99 == Settings->timezone) { snprintf_P(stemp, sizeof(stemp), PSTR("%d" ), Settings->timezone); @@ -1133,6 +1135,7 @@ void CmndStatus(void) #if defined(USE_ENERGY_SENSOR) && defined(USE_ENERGY_MARGIN_DETECTION) if (TasmotaGlobal.energy_driver) { + // Status 9 - StatusPTH if ((0 == payload) || (9 == payload)) { EnergyMarginStatus(); CmndStatusResponse(9); @@ -1169,7 +1172,7 @@ void CmndStatus(void) #endif // FIRMWARE_MINIMAL #ifdef USE_SHUTTER - // Status 13 + // Status 13 - StatusSHT if ((0 == payload) || (13 == payload)) { if (ShutterStatus()) { CmndStatusResponse(13); } } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_120_xyzmodem.ino b/tasmota/tasmota_xdrv_driver/xdrv_120_xyzmodem.ino index e18e6cf34..ff97b4ef4 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_120_xyzmodem.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_120_xyzmodem.ino @@ -12,8 +12,10 @@ * * Usage: * Use a tool able supporting XModem file transfer and to connect either using serial or telnet with Tasmota. - * TeraTerm + * TeraTerm (When using telnet enable non-bare CR support with Tasmota command `Teraterm 1`) * SyncTerm + * SecureCRT (When using Telnet set all Telnet Advanced Options OFF EXCEPT 'Server requires bare CR`) + * * To start XModem file transfer first execute Tasmota command XSend or XReceive, * then start tool XModem receive or send. * @@ -37,6 +39,8 @@ #define XDRV_120 120 +//#define XYZM_DEBUG + #define XYZM_SOH 0x01 // Start of 128 byte data #define XYZM_STX 0x02 // Start of 1024 byte data #define XYZM_EOT 0x04 @@ -51,7 +55,7 @@ // Number of seconds until giving up hope of receiving sync packets from host. const uint8_t XYZMODEM_SYNC_TIMEOUT = 30; const uint8_t XYZMODEM_RECV_TIMEOUT_SHORT = 1; // Protocol = 10 seconds -const uint8_t XYZMODEM_RECV_TIMEOUT_LONG = 20; // Protocol = 60 seconds +const uint8_t XYZMODEM_RECV_TIMEOUT_LONG = 3; // Protocol = 60 seconds // Number of times we try to send a packet to the host until we give up sending.. const uint8_t XYZMODEM_MAX_RETRY = 4; // Protocol = 10 for total packets to be send. Here retry per packet @@ -353,23 +357,51 @@ bool XYZModemReadAvailable(uint32_t timeout) { } int XYZModemReadByte(void) { - if (!XYZModemReadAvailable(XYZModem.receive_timeout)) { - return -1; - } - int in_char = XYZModemRead(); - if (in_char >= 0) { - if (TXMP_TELNET == XYZModem.protocol) { - if (0xFF == in_char) { // Fix XModem over Telnet escape - XYZModemRead(); - } - if (XYZModem.teraterm && (0x0D == in_char)) { // Fix TeraTerm - XYZModemRead(); - } + uint8_t telnet_buffer[3]; + while (true) { + if (!XYZModemReadAvailable(XYZModem.receive_timeout)) { + return -1; } + int in_char = XYZModemRead(); +#ifdef XYZM_DEBUG + Serial.printf("%02X",in_char); +#endif + if (in_char >= 0) { + if (TXMP_TELNET == XYZModem.protocol) { + if (0xFF == in_char) { // Telnet IAC - Fix XModem over Telnet escape + telnet_buffer[0] = in_char; + int in_char2 = XYZModemRead(); +#ifdef XYZM_DEBUG + Serial.printf("[%02X]",in_char2); +#endif + if ((in_char2 >= 251) && (in_char2 <= 254)) { // Telnet IAC - WILL, DO, WONT, DONT + if (251 == in_char2) { // 251 = 0xFB = WILL + telnet_buffer[1] = 254; // 254 = 0xFE = DONT + } + else if (253 == in_char2) { // 253 = 0xFD = DO + telnet_buffer[1] = 252; // 252 = 0xFC = WONT + } + int in_char3 = XYZModemRead(); + telnet_buffer[2] = in_char3; +#ifdef XYZM_DEBUG + Serial.printf("(%02X)",in_char3); +#endif + XYZModemWriteBuf(telnet_buffer, 3); // Send not supported telnet functions reponse(s) + continue; + } + } + if (XYZModem.teraterm && (0x0D == in_char)) { // Fix TeraTerm + int in_char2 = XYZModemRead(); +#ifdef XYZM_DEBUG + Serial.printf("[%02X]",in_char2); +#endif + } + } + return in_char; + } + break; } -// AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Rcvd %d"), in_char); - - return in_char; + return -1; } /*********************************************************************************************\ @@ -499,7 +531,7 @@ void XYZModemSendNakOrC(void) { XYZModemWrite(out_char); } -void XYZModemSendNak(void) { +bool XYZModemSendNak(void) { // When the receiver wishes to , it should call a "PURGE" subroutine, to wait // for the line to clear. Recall the sender tosses any characters in its buffer // immediately upon completing sending a block, to ensure no glitches were mis-interpreted. @@ -516,13 +548,14 @@ void XYZModemSendNak(void) { XYZModem.nak_count--; if (0 == XYZModem.nak_count) { XYZModemCancel(); // Cancel xfer - return; + return false; } } XYZModemWrite(XYZM_NAK); XYZModem.mode = XYZD_SOH; + return true; } bool XYZModemCheckPacket(uint8_t *buffer) { @@ -630,21 +663,43 @@ int XYZModemReceive(uint32_t packet_no) { } case XYZD_BLK1: { // AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: XYZD_BLK1")); - +/* + // Needed with SecureCRT unless Session Option - Telnet - Server requires bare CR is set + if (TXMP_TELNET == XYZModem.protocol) { + if (0x0D == in_char) { + int in_char2 = XYZModemRead(); +#ifdef XYZM_DEBUG + Serial.printf("[%02X]",in_char2); +#endif + } + } +*/ xmodem_buffer[1] = in_char; XYZModem.mode = XYZD_BLK2; break; } case XYZD_BLK2: { // AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: XYZD_BLK2 %02X, exor %02X"), in_char, (in_char ^ xmodem_buffer[1])); - +/* + // Needed with SecureCRT unless Session Option - Telnet - Server requires bare CR is set + if (TXMP_TELNET == XYZModem.protocol) { + if (0x0D == in_char) { + int in_char2 = XYZModemRead(); +#ifdef XYZM_DEBUG + Serial.printf("[%02X]",in_char2); +#endif + } + } +*/ xmodem_buffer[2] = in_char; if (0xFF == (in_char ^ xmodem_buffer[1])) { xmodem_buffer_ptr = 3; packet_size = 3 + XYZModem.packet_size + ((XYZModem.oldChecksum) ? 1 : 2); XYZModem.mode = XYZD_DATA; } else { - XYZModemSendNak(); + if (!XYZModemSendNak()) { + return XYZS_PACKET; + } } break; } @@ -653,7 +708,6 @@ int XYZModemReceive(uint32_t packet_no) { xmodem_buffer[xmodem_buffer_ptr++] = in_char; if (xmodem_buffer_ptr >= packet_size) { -// XYZFile.byte_counter += XYZModem.packet_size; XYZFile.byte_counter = packet_no * XYZModem.packet_size; XYZModem.mode = XYZD_SOH; packet_ready = true; @@ -806,6 +860,9 @@ bool XYZModemLoop(void) { } int result = XYZModemReceive(XYZModem.packet_no); if (result) { +#ifdef XYZM_DEBUG + Serial.println(); +#endif switch (result) { case XYZS_EOT: { XYZModemFileWriteEot(1); @@ -824,6 +881,7 @@ bool XYZModemLoop(void) { // Receive character timeout - will retry AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Timeout - retry")); XYZModem.retry--; + XYZFile.step = XYZM_RECEIVE; break; } case XYZS_OTHER: { @@ -863,6 +921,9 @@ bool XYZModemLoop(void) { return true; } } else { +#ifdef XYZM_DEBUG + Serial.println(); +#endif XYZModem.packet_no++; } break; @@ -916,7 +977,6 @@ void CmndXSend(void) { if (!strcasecmp_P(XdrvMailbox.data, PSTR("Settings"))) { XYZFile.size = sizeof(TSettings); XYZFile.file = false; -// XYZModem.enabled = XYZM_SEND; ResponseCmndChar("Ready to start receive Settings"); #ifdef USE_UFILESYS } else { @@ -925,7 +985,6 @@ void CmndXSend(void) { XYZFile.size = TfsFileSize(XYZFile.file_name); if (XYZFile.size) { XYZFile.file = true; -// XYZModem.enabled = XYZM_SEND; ResponseCmndChar("Ready to start receive file"); } else { ResponseCmndChar("File is empty");