fix: prevent Serial.printf from blocking when USB disconnected
All checks were successful
CI / build (push) Successful in 2m23s
All checks were successful
CI / build (push) Successful in 2m23s
On ESP32-C3 with USB CDC, Serial.printf() blocks indefinitely when USB is not connected. This caused device freezes when booted without USB. Solution: Call Serial.setTxTimeoutMs(0) after Serial.begin() to make all Serial output non-blocking. Also added if (Serial) guards to high-traffic logging paths in EpubReaderActivity as belt-and-suspenders protection. Includes documentation of the debugging process and Serial call inventory. Also applies clang-format to fix pre-existing formatting issues.
This commit is contained in:
@@ -20,11 +20,10 @@ constexpr int MAX_BOOKMARKS_PER_BOOK = 100;
|
||||
// Get cache directory path for a book (same logic as BookManager)
|
||||
std::string getCacheDir(const std::string& bookPath) {
|
||||
const size_t hash = std::hash<std::string>{}(bookPath);
|
||||
|
||||
|
||||
if (StringUtils::checkFileExtension(bookPath, ".epub")) {
|
||||
return "/.crosspoint/epub_" + std::to_string(hash);
|
||||
} else if (StringUtils::checkFileExtension(bookPath, ".txt") ||
|
||||
StringUtils::checkFileExtension(bookPath, ".TXT") ||
|
||||
} else if (StringUtils::checkFileExtension(bookPath, ".txt") || StringUtils::checkFileExtension(bookPath, ".TXT") ||
|
||||
StringUtils::checkFileExtension(bookPath, ".md")) {
|
||||
return "/.crosspoint/txt_" + std::to_string(hash);
|
||||
}
|
||||
@@ -47,21 +46,21 @@ std::vector<Bookmark> BookmarkStore::getBookmarks(const std::string& bookPath) {
|
||||
bool BookmarkStore::addBookmark(const std::string& bookPath, const Bookmark& bookmark) {
|
||||
std::vector<Bookmark> bookmarks;
|
||||
loadBookmarks(bookPath, bookmarks);
|
||||
|
||||
|
||||
// Check if bookmark already exists at this location
|
||||
auto it = std::find_if(bookmarks.begin(), bookmarks.end(), [&](const Bookmark& b) {
|
||||
return b.spineIndex == bookmark.spineIndex && b.contentOffset == bookmark.contentOffset;
|
||||
});
|
||||
|
||||
|
||||
if (it != bookmarks.end()) {
|
||||
Serial.printf("[%lu] [BMS] Bookmark already exists at spine %u, offset %u\n",
|
||||
millis(), bookmark.spineIndex, bookmark.contentOffset);
|
||||
Serial.printf("[%lu] [BMS] Bookmark already exists at spine %u, offset %u\n", millis(), bookmark.spineIndex,
|
||||
bookmark.contentOffset);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Add new bookmark
|
||||
bookmarks.push_back(bookmark);
|
||||
|
||||
|
||||
// Trim to max size (remove oldest)
|
||||
if (bookmarks.size() > MAX_BOOKMARKS_PER_BOOK) {
|
||||
// Sort by timestamp and remove oldest
|
||||
@@ -70,100 +69,99 @@ bool BookmarkStore::addBookmark(const std::string& bookPath, const Bookmark& boo
|
||||
});
|
||||
bookmarks.resize(MAX_BOOKMARKS_PER_BOOK);
|
||||
}
|
||||
|
||||
|
||||
return saveBookmarks(bookPath, bookmarks);
|
||||
}
|
||||
|
||||
bool BookmarkStore::removeBookmark(const std::string& bookPath, uint16_t spineIndex, uint32_t contentOffset) {
|
||||
std::vector<Bookmark> bookmarks;
|
||||
loadBookmarks(bookPath, bookmarks);
|
||||
|
||||
|
||||
auto it = std::find_if(bookmarks.begin(), bookmarks.end(), [&](const Bookmark& b) {
|
||||
return b.spineIndex == spineIndex && b.contentOffset == contentOffset;
|
||||
});
|
||||
|
||||
|
||||
if (it == bookmarks.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bookmarks.erase(it);
|
||||
Serial.printf("[%lu] [BMS] Removed bookmark at spine %u, offset %u\n", millis(), spineIndex, contentOffset);
|
||||
|
||||
|
||||
return saveBookmarks(bookPath, bookmarks);
|
||||
}
|
||||
|
||||
bool BookmarkStore::isPageBookmarked(const std::string& bookPath, uint16_t spineIndex, uint32_t contentOffset) {
|
||||
std::vector<Bookmark> bookmarks;
|
||||
loadBookmarks(bookPath, bookmarks);
|
||||
|
||||
return std::any_of(bookmarks.begin(), bookmarks.end(), [&](const Bookmark& b) {
|
||||
return b.spineIndex == spineIndex && b.contentOffset == contentOffset;
|
||||
});
|
||||
|
||||
return std::any_of(bookmarks.begin(), bookmarks.end(),
|
||||
[&](const Bookmark& b) { return b.spineIndex == spineIndex && b.contentOffset == contentOffset; });
|
||||
}
|
||||
|
||||
int BookmarkStore::getBookmarkCount(const std::string& bookPath) {
|
||||
const std::string filePath = getBookmarksFilePath(bookPath);
|
||||
if (filePath.empty()) return 0;
|
||||
|
||||
|
||||
FsFile inputFile;
|
||||
if (!SdMan.openFileForRead("BMS", filePath, inputFile)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint8_t version;
|
||||
serialization::readPod(inputFile, version);
|
||||
if (version != BOOKMARKS_FILE_VERSION) {
|
||||
inputFile.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint8_t count;
|
||||
serialization::readPod(inputFile, count);
|
||||
inputFile.close();
|
||||
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
std::vector<BookmarkedBook> BookmarkStore::getBooksWithBookmarks() {
|
||||
std::vector<BookmarkedBook> result;
|
||||
|
||||
|
||||
// Scan /.crosspoint/ directory for cache folders with bookmarks
|
||||
auto crosspoint = SdMan.open("/.crosspoint");
|
||||
if (!crosspoint || !crosspoint.isDirectory()) {
|
||||
if (crosspoint) crosspoint.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
crosspoint.rewindDirectory();
|
||||
char name[256];
|
||||
|
||||
|
||||
for (auto entry = crosspoint.openNextFile(); entry; entry = crosspoint.openNextFile()) {
|
||||
entry.getName(name, sizeof(name));
|
||||
|
||||
|
||||
if (!entry.isDirectory()) {
|
||||
entry.close();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Check if this directory has a bookmarks file
|
||||
std::string dirPath = "/.crosspoint/";
|
||||
dirPath += name;
|
||||
std::string bookmarksPath = dirPath + "/" + BOOKMARKS_FILENAME;
|
||||
|
||||
|
||||
if (SdMan.exists(bookmarksPath.c_str())) {
|
||||
// Read the bookmarks file to get count and book info
|
||||
FsFile bookmarksFile;
|
||||
if (SdMan.openFileForRead("BMS", bookmarksPath, bookmarksFile)) {
|
||||
uint8_t version;
|
||||
serialization::readPod(bookmarksFile, version);
|
||||
|
||||
|
||||
if (version == BOOKMARKS_FILE_VERSION) {
|
||||
uint8_t count;
|
||||
serialization::readPod(bookmarksFile, count);
|
||||
|
||||
|
||||
// Read book metadata (stored at end of file)
|
||||
std::string bookPath, bookTitle, bookAuthor;
|
||||
|
||||
|
||||
// Skip bookmark entries to get to metadata
|
||||
for (uint8_t i = 0; i < count; i++) {
|
||||
std::string tempName;
|
||||
@@ -176,12 +174,12 @@ std::vector<BookmarkedBook> BookmarkStore::getBooksWithBookmarks() {
|
||||
serialization::readPod(bookmarksFile, tempPage);
|
||||
serialization::readPod(bookmarksFile, tempTimestamp);
|
||||
}
|
||||
|
||||
|
||||
// Read book metadata
|
||||
serialization::readString(bookmarksFile, bookPath);
|
||||
serialization::readString(bookmarksFile, bookTitle);
|
||||
serialization::readString(bookmarksFile, bookAuthor);
|
||||
|
||||
|
||||
if (!bookPath.empty() && count > 0) {
|
||||
BookmarkedBook book;
|
||||
book.path = bookPath;
|
||||
@@ -197,19 +195,18 @@ std::vector<BookmarkedBook> BookmarkStore::getBooksWithBookmarks() {
|
||||
entry.close();
|
||||
}
|
||||
crosspoint.close();
|
||||
|
||||
|
||||
// Sort by title
|
||||
std::sort(result.begin(), result.end(), [](const BookmarkedBook& a, const BookmarkedBook& b) {
|
||||
return a.title < b.title;
|
||||
});
|
||||
|
||||
std::sort(result.begin(), result.end(),
|
||||
[](const BookmarkedBook& a, const BookmarkedBook& b) { return a.title < b.title; });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void BookmarkStore::clearBookmarks(const std::string& bookPath) {
|
||||
const std::string filePath = getBookmarksFilePath(bookPath);
|
||||
if (filePath.empty()) return;
|
||||
|
||||
|
||||
SdMan.remove(filePath.c_str());
|
||||
Serial.printf("[%lu] [BMS] Cleared all bookmarks for %s\n", millis(), bookPath.c_str());
|
||||
}
|
||||
@@ -217,21 +214,21 @@ void BookmarkStore::clearBookmarks(const std::string& bookPath) {
|
||||
bool BookmarkStore::saveBookmarks(const std::string& bookPath, const std::vector<Bookmark>& bookmarks) {
|
||||
const std::string cacheDir = getCacheDir(bookPath);
|
||||
if (cacheDir.empty()) return false;
|
||||
|
||||
|
||||
// Make sure the directory exists
|
||||
SdMan.mkdir(cacheDir.c_str());
|
||||
|
||||
|
||||
const std::string filePath = cacheDir + "/" + BOOKMARKS_FILENAME;
|
||||
|
||||
|
||||
FsFile outputFile;
|
||||
if (!SdMan.openFileForWrite("BMS", filePath, outputFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
serialization::writePod(outputFile, BOOKMARKS_FILE_VERSION);
|
||||
const uint8_t count = static_cast<uint8_t>(std::min(bookmarks.size(), static_cast<size_t>(255)));
|
||||
serialization::writePod(outputFile, count);
|
||||
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
const auto& bookmark = bookmarks[i];
|
||||
serialization::writeString(outputFile, bookmark.name);
|
||||
@@ -240,7 +237,7 @@ bool BookmarkStore::saveBookmarks(const std::string& bookPath, const std::vector
|
||||
serialization::writePod(outputFile, bookmark.pageNumber);
|
||||
serialization::writePod(outputFile, bookmark.timestamp);
|
||||
}
|
||||
|
||||
|
||||
// Store book metadata at end (for getBooksWithBookmarks to read)
|
||||
// Extract title from path if we don't have it
|
||||
std::string title = bookPath;
|
||||
@@ -252,11 +249,11 @@ bool BookmarkStore::saveBookmarks(const std::string& bookPath, const std::vector
|
||||
if (dot != std::string::npos) {
|
||||
title.resize(dot);
|
||||
}
|
||||
|
||||
|
||||
serialization::writeString(outputFile, bookPath);
|
||||
serialization::writeString(outputFile, title);
|
||||
serialization::writeString(outputFile, ""); // Author (not always available)
|
||||
|
||||
|
||||
outputFile.close();
|
||||
Serial.printf("[%lu] [BMS] Bookmarks saved for %s (%d entries)\n", millis(), bookPath.c_str(), count);
|
||||
return true;
|
||||
@@ -264,15 +261,15 @@ bool BookmarkStore::saveBookmarks(const std::string& bookPath, const std::vector
|
||||
|
||||
bool BookmarkStore::loadBookmarks(const std::string& bookPath, std::vector<Bookmark>& bookmarks) {
|
||||
bookmarks.clear();
|
||||
|
||||
|
||||
const std::string filePath = getBookmarksFilePath(bookPath);
|
||||
if (filePath.empty()) return false;
|
||||
|
||||
|
||||
FsFile inputFile;
|
||||
if (!SdMan.openFileForRead("BMS", filePath, inputFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
uint8_t version;
|
||||
serialization::readPod(inputFile, version);
|
||||
if (version != BOOKMARKS_FILE_VERSION) {
|
||||
@@ -280,11 +277,11 @@ bool BookmarkStore::loadBookmarks(const std::string& bookPath, std::vector<Bookm
|
||||
inputFile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
uint8_t count;
|
||||
serialization::readPod(inputFile, count);
|
||||
bookmarks.reserve(count);
|
||||
|
||||
|
||||
for (uint8_t i = 0; i < count; i++) {
|
||||
Bookmark bookmark;
|
||||
serialization::readString(inputFile, bookmark.name);
|
||||
@@ -294,7 +291,7 @@ bool BookmarkStore::loadBookmarks(const std::string& bookPath, std::vector<Bookm
|
||||
serialization::readPod(inputFile, bookmark.timestamp);
|
||||
bookmarks.push_back(bookmark);
|
||||
}
|
||||
|
||||
|
||||
inputFile.close();
|
||||
Serial.printf("[%lu] [BMS] Bookmarks loaded for %s (%d entries)\n", millis(), bookPath.c_str(), count);
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user