diff --git a/src/activities/network/CalibreWirelessActivity.cpp b/src/activities/network/CalibreWirelessActivity.cpp index 3b54d87d..fc667866 100644 --- a/src/activities/network/CalibreWirelessActivity.cpp +++ b/src/activities/network/CalibreWirelessActivity.cpp @@ -16,6 +16,45 @@ namespace { constexpr uint16_t UDP_PORTS[] = {54982, 48123, 39001, 44044, 59678}; constexpr uint16_t LOCAL_UDP_PORT = 8134; // Port to receive responses + +// Write buffer for batched SD writes (improves throughput by reducing write calls) +constexpr size_t WRITE_BUFFER_SIZE = 4096; // 4KB buffer +static uint8_t writeBuffer[WRITE_BUFFER_SIZE]; +static size_t writeBufferPos = 0; +static File* currentWriteFile = nullptr; + +static bool flushWriteBuffer() { + if (writeBufferPos > 0 && currentWriteFile && *currentWriteFile) { + esp_task_wdt_reset(); + const size_t written = currentWriteFile->write(writeBuffer, writeBufferPos); + esp_task_wdt_reset(); + if (written != writeBufferPos) { + writeBufferPos = 0; + return false; + } + writeBufferPos = 0; + } + return true; +} + +static bool bufferedWrite(const uint8_t* data, size_t len) { + while (len > 0) { + const size_t space = WRITE_BUFFER_SIZE - writeBufferPos; + const size_t toCopy = std::min(space, len); + + memcpy(writeBuffer + writeBufferPos, data, toCopy); + writeBufferPos += toCopy; + data += toCopy; + len -= toCopy; + + if (writeBufferPos >= WRITE_BUFFER_SIZE) { + if (!flushWriteBuffer()) { + return false; + } + } + } + return true; +} } // namespace void CalibreWirelessActivity::displayTaskTrampoline(void* param) { @@ -80,7 +119,9 @@ void CalibreWirelessActivity::onExit() { tcpClient.stop(); } - // Close any open file to prevent corruption + // Flush write buffer and close any open file to prevent corruption + flushWriteBuffer(); + currentWriteFile = nullptr; if (currentFile) { Serial.printf("[%lu] [CAL] Closing open file...\n", millis()); currentFile.flush(); @@ -374,6 +415,7 @@ bool CalibreWirelessActivity::readJsonMessage(std::string& message) { } // Read in chunks char buf[1024]; + int iterations = 0; while (available > 0) { int toRead = std::min(available, static_cast(sizeof(buf))); int bytesRead = tcpClient.read(reinterpret_cast(buf), toRead); @@ -383,6 +425,10 @@ bool CalibreWirelessActivity::readJsonMessage(std::string& message) { } else { break; } + // Reset watchdog every 16 iterations to prevent timeout during large reads + if ((++iterations & 0xF) == 0) { + esp_task_wdt_reset(); + } } } @@ -655,12 +701,18 @@ void CalibreWirelessActivity::handleSendBook(const std::string& data) { setState(WirelessState::RECEIVING); setStatus("Receiving: " + filename); - // Open file for writing + // Open file for writing - reset watchdog as FAT allocation can be slow + esp_task_wdt_reset(); if (!SdMan.openFileForWrite("CAL", currentFilename.c_str(), currentFile)) { setError("Failed to create file"); sendJsonResponse(OpCode::ERROR, "{\"message\":\"Failed to create file\"}"); return; } + esp_task_wdt_reset(); + + // Initialize write buffer + currentWriteFile = ¤tFile; + writeBufferPos = 0; // Send OK to start receiving binary data sendJsonResponse(OpCode::OK, "{}"); @@ -672,11 +724,12 @@ void CalibreWirelessActivity::handleSendBook(const std::string& data) { // Check if recvBuffer has leftover data (binary file data that arrived with the JSON) if (!recvBuffer.empty()) { size_t toWrite = std::min(recvBuffer.size(), binaryBytesRemaining); - size_t written = currentFile.write(reinterpret_cast(recvBuffer.data()), toWrite); - bytesReceived += written; - binaryBytesRemaining -= written; - recvBuffer = recvBuffer.substr(toWrite); - updateRequired = true; + if (bufferedWrite(reinterpret_cast(recvBuffer.data()), toWrite)) { + bytesReceived += toWrite; + binaryBytesRemaining -= toWrite; + recvBuffer = recvBuffer.substr(toWrite); + updateRequired = true; + } } } @@ -708,6 +761,8 @@ void CalibreWirelessActivity::receiveBinaryData() { if (available == 0) { // Check if connection is still alive if (!tcpClient.connected()) { + flushWriteBuffer(); + currentWriteFile = nullptr; currentFile.close(); inBinaryMode = false; setError("Transfer interrupted"); @@ -715,18 +770,34 @@ void CalibreWirelessActivity::receiveBinaryData() { return; } - uint8_t buffer[1024]; + // Use 4KB buffer for network reads + uint8_t buffer[4096]; const size_t toRead = std::min(sizeof(buffer), binaryBytesRemaining); + + // Reset watchdog before network read + esp_task_wdt_reset(); const size_t bytesRead = tcpClient.read(buffer, toRead); if (bytesRead > 0) { - currentFile.write(buffer, bytesRead); + // Use buffered write for better throughput + if (!bufferedWrite(buffer, bytesRead)) { + flushWriteBuffer(); + currentWriteFile = nullptr; + currentFile.close(); + inBinaryMode = false; + setError("Write error"); + return; + } + bytesReceived += bytesRead; binaryBytesRemaining -= bytesRead; updateRequired = true; if (binaryBytesRemaining == 0) { - // Transfer complete + // Transfer complete - flush remaining buffer + esp_task_wdt_reset(); + flushWriteBuffer(); + currentWriteFile = nullptr; currentFile.flush(); currentFile.close(); inBinaryMode = false;