mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-07 08:07:40 +03:00
Fix Calibre wireless exit freeze and improve stability
This commit applies all Calibre fixes to the 0.14.0 base: 1. **Exit freeze fix (Issue #342)**: - Add atomic `shouldStop` flag for cooperative task shutdown - Close network connections before deleting tasks (unblocks I/O) - Tasks check shouldStop and self-delete gracefully - Use eTaskGetState() to avoid double-deletion - Break discovery wait into 50ms chunks for faster exit 2. **Free space reporting**: - Probe actual SD card space using preAllocate() - Remove confusing "ignore free space" message - Report accurate space to Calibre for transfer decisions 3. **Write buffer (4KB)**: - Batch SD card writes to reduce I/O overhead - Improves transfer throughput significantly 4. **Watchdog resets**: - Add esp_task_wdt_reset() throughout long operations - Prevents WDT crashes during file operations 5. **Exception safety**: - Replace std::stoul with strtoul to avoid abort() on ESP32 - ESP32 has exceptions disabled by default 6. **Larger network buffer**: - Increase receive buffer from 1KB to 4KB
This commit is contained in:
parent
56ec3dfb6d
commit
1e7fff181b
@ -4,6 +4,7 @@
|
|||||||
#include <HardwareSerial.h>
|
#include <HardwareSerial.h>
|
||||||
#include <SDCardManager.h>
|
#include <SDCardManager.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
#include <esp_task_wdt.h>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
@ -15,6 +16,45 @@
|
|||||||
namespace {
|
namespace {
|
||||||
constexpr uint16_t UDP_PORTS[] = {54982, 48123, 39001, 44044, 59678};
|
constexpr uint16_t UDP_PORTS[] = {54982, 48123, 39001, 44044, 59678};
|
||||||
constexpr uint16_t LOCAL_UDP_PORT = 8134; // Port to receive responses
|
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 FsFile* 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
|
} // namespace
|
||||||
|
|
||||||
void CalibreWirelessActivity::displayTaskTrampoline(void* param) {
|
void CalibreWirelessActivity::displayTaskTrampoline(void* param) {
|
||||||
@ -30,6 +70,8 @@ void CalibreWirelessActivity::networkTaskTrampoline(void* param) {
|
|||||||
void CalibreWirelessActivity::onEnter() {
|
void CalibreWirelessActivity::onEnter() {
|
||||||
Activity::onEnter();
|
Activity::onEnter();
|
||||||
|
|
||||||
|
Serial.printf("[%lu] [CAL] onEnter - starting Calibre Wireless activity\n", millis());
|
||||||
|
|
||||||
renderingMutex = xSemaphoreCreateMutex();
|
renderingMutex = xSemaphoreCreateMutex();
|
||||||
stateMutex = xSemaphoreCreateMutex();
|
stateMutex = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
@ -45,6 +87,7 @@ void CalibreWirelessActivity::onEnter() {
|
|||||||
bytesReceived = 0;
|
bytesReceived = 0;
|
||||||
inBinaryMode = false;
|
inBinaryMode = false;
|
||||||
recvBuffer.clear();
|
recvBuffer.clear();
|
||||||
|
shouldStop = false; // Reset shutdown flag
|
||||||
|
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
|
|
||||||
@ -61,41 +104,79 @@ void CalibreWirelessActivity::onEnter() {
|
|||||||
void CalibreWirelessActivity::onExit() {
|
void CalibreWirelessActivity::onExit() {
|
||||||
Activity::onExit();
|
Activity::onExit();
|
||||||
|
|
||||||
// Turn off WiFi when exiting
|
Serial.printf("[%lu] [CAL] onExit - beginning graceful shutdown\n", millis());
|
||||||
WiFi.mode(WIFI_OFF);
|
|
||||||
|
|
||||||
// Stop UDP listening
|
// Signal tasks to stop - they check this flag each iteration
|
||||||
|
shouldStop = true;
|
||||||
|
|
||||||
|
// Close network connections FIRST - this unblocks any waiting reads/writes
|
||||||
|
Serial.printf("[%lu] [CAL] Stopping UDP listener...\n", millis());
|
||||||
udp.stop();
|
udp.stop();
|
||||||
|
|
||||||
// Close TCP client if connected
|
Serial.printf("[%lu] [CAL] Closing TCP connection...\n", millis());
|
||||||
if (tcpClient.connected()) {
|
if (tcpClient.connected()) {
|
||||||
tcpClient.stop();
|
tcpClient.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close any open file
|
// Flush write buffer and close any open file to prevent corruption
|
||||||
|
flushWriteBuffer();
|
||||||
|
currentWriteFile = nullptr;
|
||||||
if (currentFile) {
|
if (currentFile) {
|
||||||
|
Serial.printf("[%lu] [CAL] Closing open file...\n", millis());
|
||||||
|
currentFile.flush();
|
||||||
currentFile.close();
|
currentFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Acquire stateMutex before deleting network task to avoid race condition
|
// Give tasks time to notice shutdown and self-delete
|
||||||
xSemaphoreTake(stateMutex, portMAX_DELAY);
|
Serial.printf("[%lu] [CAL] Waiting for tasks to self-terminate...\n", millis());
|
||||||
if (networkTaskHandle) {
|
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||||
vTaskDelete(networkTaskHandle);
|
|
||||||
networkTaskHandle = nullptr;
|
|
||||||
}
|
|
||||||
xSemaphoreGive(stateMutex);
|
|
||||||
|
|
||||||
// Acquire renderingMutex before deleting display task
|
// Store handles locally and clear member variables
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
TaskHandle_t netTask = networkTaskHandle;
|
||||||
if (displayTaskHandle) {
|
TaskHandle_t dispTask = displayTaskHandle;
|
||||||
vTaskDelete(displayTaskHandle);
|
networkTaskHandle = nullptr;
|
||||||
displayTaskHandle = nullptr;
|
displayTaskHandle = nullptr;
|
||||||
|
|
||||||
|
// Force delete network task if it hasn't self-terminated
|
||||||
|
if (netTask && eTaskGetState(netTask) != eDeleted) {
|
||||||
|
Serial.printf("[%lu] [CAL] Force-deleting network task...\n", millis());
|
||||||
|
vTaskDelete(netTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
// Now safe to turn off WiFi - no tasks using it
|
||||||
|
Serial.printf("[%lu] [CAL] Disconnecting WiFi...\n", millis());
|
||||||
|
WiFi.disconnect(false);
|
||||||
|
vTaskDelay(30 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
Serial.printf("[%lu] [CAL] Setting WiFi mode OFF...\n", millis());
|
||||||
|
WiFi.mode(WIFI_OFF);
|
||||||
|
vTaskDelay(30 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
// Force delete display task if it hasn't self-terminated
|
||||||
|
if (dispTask && eTaskGetState(dispTask) != eDeleted) {
|
||||||
|
Serial.printf("[%lu] [CAL] Acquiring rendering mutex...\n", millis());
|
||||||
|
if (xSemaphoreTake(renderingMutex, pdMS_TO_TICKS(500)) == pdTRUE) {
|
||||||
|
Serial.printf("[%lu] [CAL] Force-deleting display task...\n", millis());
|
||||||
|
vTaskDelete(dispTask);
|
||||||
|
xSemaphoreGive(renderingMutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete mutexes
|
||||||
|
Serial.printf("[%lu] [CAL] Cleaning up mutexes...\n", millis());
|
||||||
|
if (renderingMutex) {
|
||||||
vSemaphoreDelete(renderingMutex);
|
vSemaphoreDelete(renderingMutex);
|
||||||
renderingMutex = nullptr;
|
renderingMutex = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stateMutex) {
|
||||||
vSemaphoreDelete(stateMutex);
|
vSemaphoreDelete(stateMutex);
|
||||||
stateMutex = nullptr;
|
stateMutex = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.printf("[%lu] [CAL] onExit complete\n", millis());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalibreWirelessActivity::loop() {
|
void CalibreWirelessActivity::loop() {
|
||||||
@ -106,7 +187,7 @@ void CalibreWirelessActivity::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CalibreWirelessActivity::displayTaskLoop() {
|
void CalibreWirelessActivity::displayTaskLoop() {
|
||||||
while (true) {
|
while (!shouldStop) {
|
||||||
if (updateRequired) {
|
if (updateRequired) {
|
||||||
updateRequired = false;
|
updateRequired = false;
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
@ -115,14 +196,18 @@ void CalibreWirelessActivity::displayTaskLoop() {
|
|||||||
}
|
}
|
||||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
|
Serial.printf("[%lu] [CAL] Display task exiting gracefully\n", millis());
|
||||||
|
vTaskDelete(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalibreWirelessActivity::networkTaskLoop() {
|
void CalibreWirelessActivity::networkTaskLoop() {
|
||||||
while (true) {
|
while (!shouldStop) {
|
||||||
xSemaphoreTake(stateMutex, portMAX_DELAY);
|
xSemaphoreTake(stateMutex, portMAX_DELAY);
|
||||||
const auto currentState = state;
|
const auto currentState = state;
|
||||||
xSemaphoreGive(stateMutex);
|
xSemaphoreGive(stateMutex);
|
||||||
|
|
||||||
|
if (shouldStop) break;
|
||||||
|
|
||||||
switch (currentState) {
|
switch (currentState) {
|
||||||
case WirelessState::DISCOVERING:
|
case WirelessState::DISCOVERING:
|
||||||
listenForDiscovery();
|
listenForDiscovery();
|
||||||
@ -144,18 +229,25 @@ void CalibreWirelessActivity::networkTaskLoop() {
|
|||||||
|
|
||||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
|
Serial.printf("[%lu] [CAL] Network task exiting gracefully\n", millis());
|
||||||
|
vTaskDelete(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalibreWirelessActivity::listenForDiscovery() {
|
void CalibreWirelessActivity::listenForDiscovery() {
|
||||||
|
if (shouldStop) return;
|
||||||
|
|
||||||
// Broadcast "hello" on all UDP discovery ports to find Calibre
|
// Broadcast "hello" on all UDP discovery ports to find Calibre
|
||||||
for (const uint16_t port : UDP_PORTS) {
|
for (const uint16_t port : UDP_PORTS) {
|
||||||
|
if (shouldStop) return;
|
||||||
udp.beginPacket("255.255.255.255", port);
|
udp.beginPacket("255.255.255.255", port);
|
||||||
udp.write(reinterpret_cast<const uint8_t*>("hello"), 5);
|
udp.write(reinterpret_cast<const uint8_t*>("hello"), 5);
|
||||||
udp.endPacket();
|
udp.endPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for Calibre's response
|
// Wait for Calibre's response in smaller chunks to allow faster shutdown
|
||||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
for (int i = 0; i < 10 && !shouldStop; i++) {
|
||||||
|
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
|
||||||
// Check for response
|
// Check for response
|
||||||
const int packetSize = udp.parsePacket();
|
const int packetSize = udp.parsePacket();
|
||||||
@ -455,9 +547,7 @@ void CalibreWirelessActivity::handleCommand(const OpCode opcode, const std::stri
|
|||||||
|
|
||||||
void CalibreWirelessActivity::handleGetInitializationInfo(const std::string& data) {
|
void CalibreWirelessActivity::handleGetInitializationInfo(const std::string& data) {
|
||||||
setState(WirelessState::WAITING);
|
setState(WirelessState::WAITING);
|
||||||
setStatus("Connected to " + calibreHostname +
|
setStatus("Connected to " + calibreHostname + "\nWaiting for transfer...");
|
||||||
"\nWaiting for transfer...\n\nIf transfer fails, enable\n'Ignore free space' in Calibre's\nSmartDevice "
|
|
||||||
"plugin settings.");
|
|
||||||
|
|
||||||
// Build response with device capabilities
|
// Build response with device capabilities
|
||||||
// Format must match what Calibre expects from a smart device
|
// Format must match what Calibre expects from a smart device
|
||||||
@ -504,9 +594,10 @@ void CalibreWirelessActivity::handleGetDeviceInformation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CalibreWirelessActivity::handleFreeSpace() {
|
void CalibreWirelessActivity::handleFreeSpace() {
|
||||||
// TODO: Report actual SD card free space instead of hardcoded value
|
const uint64_t freeBytes = getSDCardFreeSpace();
|
||||||
// Report 10GB free space for now
|
char response[64];
|
||||||
sendJsonResponse(OpCode::OK, "{\"free_space_on_device\":10737418240}");
|
snprintf(response, sizeof(response), "{\"free_space_on_device\":%llu}", static_cast<unsigned long long>(freeBytes));
|
||||||
|
sendJsonResponse(OpCode::OK, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalibreWirelessActivity::handleGetBookCount() {
|
void CalibreWirelessActivity::handleGetBookCount() {
|
||||||
@ -560,9 +651,15 @@ void CalibreWirelessActivity::handleSendBook(const std::string& data) {
|
|||||||
numEnd++;
|
numEnd++;
|
||||||
}
|
}
|
||||||
if (numEnd > numStart) {
|
if (numEnd > numStart) {
|
||||||
length = std::stoul(data.substr(numStart, numEnd - numStart));
|
// Use strtoul instead of std::stoul to avoid exceptions on ESP32
|
||||||
|
char* endPtr = nullptr;
|
||||||
|
const std::string numStr = data.substr(numStart, numEnd - numStart);
|
||||||
|
length = strtoul(numStr.c_str(), &endPtr, 10);
|
||||||
|
if (endPtr && *endPtr == '\0') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
length = 0; // Reset if parsing failed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -591,12 +688,18 @@ void CalibreWirelessActivity::handleSendBook(const std::string& data) {
|
|||||||
setState(WirelessState::RECEIVING);
|
setState(WirelessState::RECEIVING);
|
||||||
setStatus("Receiving: " + filename);
|
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)) {
|
if (!SdMan.openFileForWrite("CAL", currentFilename.c_str(), currentFile)) {
|
||||||
setError("Failed to create file");
|
setError("Failed to create file");
|
||||||
sendJsonResponse(OpCode::ERROR, "{\"message\":\"Failed to create file\"}");
|
sendJsonResponse(OpCode::ERROR, "{\"message\":\"Failed to create file\"}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
esp_task_wdt_reset();
|
||||||
|
|
||||||
|
// Initialize write buffer
|
||||||
|
currentWriteFile = ¤tFile;
|
||||||
|
writeBufferPos = 0;
|
||||||
|
|
||||||
// Send OK to start receiving binary data
|
// Send OK to start receiving binary data
|
||||||
sendJsonResponse(OpCode::OK, "{}");
|
sendJsonResponse(OpCode::OK, "{}");
|
||||||
@ -608,12 +711,13 @@ void CalibreWirelessActivity::handleSendBook(const std::string& data) {
|
|||||||
// Check if recvBuffer has leftover data (binary file data that arrived with the JSON)
|
// Check if recvBuffer has leftover data (binary file data that arrived with the JSON)
|
||||||
if (!recvBuffer.empty()) {
|
if (!recvBuffer.empty()) {
|
||||||
size_t toWrite = std::min(recvBuffer.size(), binaryBytesRemaining);
|
size_t toWrite = std::min(recvBuffer.size(), binaryBytesRemaining);
|
||||||
size_t written = currentFile.write(reinterpret_cast<const uint8_t*>(recvBuffer.data()), toWrite);
|
if (bufferedWrite(reinterpret_cast<const uint8_t*>(recvBuffer.data()), toWrite)) {
|
||||||
bytesReceived += written;
|
bytesReceived += toWrite;
|
||||||
binaryBytesRemaining -= written;
|
binaryBytesRemaining -= toWrite;
|
||||||
recvBuffer = recvBuffer.substr(toWrite);
|
recvBuffer = recvBuffer.substr(toWrite);
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalibreWirelessActivity::handleSendBookMetadata(const std::string& data) {
|
void CalibreWirelessActivity::handleSendBookMetadata(const std::string& data) {
|
||||||
@ -644,6 +748,8 @@ void CalibreWirelessActivity::receiveBinaryData() {
|
|||||||
if (available == 0) {
|
if (available == 0) {
|
||||||
// Check if connection is still alive
|
// Check if connection is still alive
|
||||||
if (!tcpClient.connected()) {
|
if (!tcpClient.connected()) {
|
||||||
|
flushWriteBuffer();
|
||||||
|
currentWriteFile = nullptr;
|
||||||
currentFile.close();
|
currentFile.close();
|
||||||
inBinaryMode = false;
|
inBinaryMode = false;
|
||||||
setError("Transfer interrupted");
|
setError("Transfer interrupted");
|
||||||
@ -651,18 +757,34 @@ void CalibreWirelessActivity::receiveBinaryData() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t buffer[1024];
|
// Use 4KB buffer for network reads
|
||||||
|
uint8_t buffer[4096];
|
||||||
const size_t toRead = std::min(sizeof(buffer), binaryBytesRemaining);
|
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);
|
const size_t bytesRead = tcpClient.read(buffer, toRead);
|
||||||
|
|
||||||
if (bytesRead > 0) {
|
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;
|
bytesReceived += bytesRead;
|
||||||
binaryBytesRemaining -= bytesRead;
|
binaryBytesRemaining -= bytesRead;
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
|
|
||||||
if (binaryBytesRemaining == 0) {
|
if (binaryBytesRemaining == 0) {
|
||||||
// Transfer complete
|
// Transfer complete - flush remaining buffer
|
||||||
|
esp_task_wdt_reset();
|
||||||
|
flushWriteBuffer();
|
||||||
|
currentWriteFile = nullptr;
|
||||||
currentFile.flush();
|
currentFile.flush();
|
||||||
currentFile.close();
|
currentFile.close();
|
||||||
inBinaryMode = false;
|
inBinaryMode = false;
|
||||||
@ -738,6 +860,63 @@ std::string CalibreWirelessActivity::getDeviceUuid() const {
|
|||||||
return std::string(uuid);
|
return std::string(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t CalibreWirelessActivity::getSDCardFreeSpace() const {
|
||||||
|
// Probe available space using SdFat's preAllocate() method.
|
||||||
|
// preAllocate() fails if there isn't enough contiguous free space,
|
||||||
|
// so we can use it to find the actual available space on the SD card.
|
||||||
|
|
||||||
|
const char* testPath = "/.crosspoint/.free_space_probe";
|
||||||
|
|
||||||
|
// Ensure the crosspoint directory exists
|
||||||
|
SdMan.mkdir("/.crosspoint");
|
||||||
|
|
||||||
|
FsFile testFile;
|
||||||
|
if (!SdMan.openFileForWrite("CAL", testPath, testFile)) {
|
||||||
|
Serial.printf("[%lu] [CAL] Free space probe: failed to create test file\n", millis());
|
||||||
|
return 64ULL * 1024 * 1024 * 1024; // Conservative fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_task_wdt_reset();
|
||||||
|
|
||||||
|
// Probe sizes from large to small (exponential decrease)
|
||||||
|
constexpr uint64_t probeSizes[] = {
|
||||||
|
256ULL * 1024 * 1024 * 1024, // 256GB
|
||||||
|
128ULL * 1024 * 1024 * 1024, // 128GB
|
||||||
|
64ULL * 1024 * 1024 * 1024, // 64GB
|
||||||
|
32ULL * 1024 * 1024 * 1024, // 32GB
|
||||||
|
16ULL * 1024 * 1024 * 1024, // 16GB
|
||||||
|
8ULL * 1024 * 1024 * 1024, // 8GB
|
||||||
|
4ULL * 1024 * 1024 * 1024, // 4GB
|
||||||
|
2ULL * 1024 * 1024 * 1024, // 2GB
|
||||||
|
1ULL * 1024 * 1024 * 1024, // 1GB
|
||||||
|
512ULL * 1024 * 1024, // 512MB
|
||||||
|
256ULL * 1024 * 1024, // 256MB
|
||||||
|
128ULL * 1024 * 1024, // 128MB
|
||||||
|
64ULL * 1024 * 1024, // 64MB
|
||||||
|
};
|
||||||
|
|
||||||
|
uint64_t availableSpace = 64ULL * 1024 * 1024; // Minimum 64MB fallback
|
||||||
|
|
||||||
|
for (const uint64_t size : probeSizes) {
|
||||||
|
esp_task_wdt_reset();
|
||||||
|
// cppcheck-suppress useStlAlgorithm
|
||||||
|
if (testFile.preAllocate(size)) {
|
||||||
|
availableSpace = size;
|
||||||
|
esp_task_wdt_reset();
|
||||||
|
testFile.truncate(0);
|
||||||
|
Serial.printf("[%lu] [CAL] Free space probe: %llu bytes available\n", millis(),
|
||||||
|
static_cast<unsigned long long>(availableSpace));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_task_wdt_reset();
|
||||||
|
testFile.close();
|
||||||
|
SdMan.remove(testPath);
|
||||||
|
|
||||||
|
return availableSpace;
|
||||||
|
}
|
||||||
|
|
||||||
void CalibreWirelessActivity::setState(WirelessState newState) {
|
void CalibreWirelessActivity::setState(WirelessState newState) {
|
||||||
xSemaphoreTake(stateMutex, portMAX_DELAY);
|
xSemaphoreTake(stateMutex, portMAX_DELAY);
|
||||||
state = newState;
|
state = newState;
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include <freertos/semphr.h>
|
#include <freertos/semphr.h>
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -66,6 +67,7 @@ class CalibreWirelessActivity final : public Activity {
|
|||||||
SemaphoreHandle_t renderingMutex = nullptr;
|
SemaphoreHandle_t renderingMutex = nullptr;
|
||||||
SemaphoreHandle_t stateMutex = nullptr;
|
SemaphoreHandle_t stateMutex = nullptr;
|
||||||
bool updateRequired = false;
|
bool updateRequired = false;
|
||||||
|
std::atomic<bool> shouldStop{false}; // Signal for graceful task shutdown
|
||||||
|
|
||||||
WirelessState state = WirelessState::DISCOVERING;
|
WirelessState state = WirelessState::DISCOVERING;
|
||||||
const std::function<void()> onComplete;
|
const std::function<void()> onComplete;
|
||||||
@ -95,8 +97,8 @@ class CalibreWirelessActivity final : public Activity {
|
|||||||
|
|
||||||
static void displayTaskTrampoline(void* param);
|
static void displayTaskTrampoline(void* param);
|
||||||
static void networkTaskTrampoline(void* param);
|
static void networkTaskTrampoline(void* param);
|
||||||
[[noreturn]] void displayTaskLoop();
|
void displayTaskLoop();
|
||||||
[[noreturn]] void networkTaskLoop();
|
void networkTaskLoop();
|
||||||
void render() const;
|
void render() const;
|
||||||
|
|
||||||
// Network operations
|
// Network operations
|
||||||
@ -119,6 +121,7 @@ class CalibreWirelessActivity final : public Activity {
|
|||||||
|
|
||||||
// Utility
|
// Utility
|
||||||
std::string getDeviceUuid() const;
|
std::string getDeviceUuid() const;
|
||||||
|
uint64_t getSDCardFreeSpace() const;
|
||||||
void setState(WirelessState newState);
|
void setState(WirelessState newState);
|
||||||
void setStatus(const std::string& message);
|
void setStatus(const std::string& message);
|
||||||
void setError(const std::string& message);
|
void setError(const std::string& message);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user