Fix XModem for SecureCRT telnet client

This commit is contained in:
Theo Arends 2025-04-24 15:48:19 +02:00
parent 0ad8696dfc
commit 115cefc557
4 changed files with 90 additions and 27 deletions

View File

@ -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 <command>|backlog <command>;...` 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

View File

@ -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 <command>|backlog <command>;...` 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

View File

@ -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); }
}

View File

@ -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 <nak>, 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");