Public Access
Storage: poll the SD card on its own task
A failed mount retries for ~2.5 s, which froze the UI every 15 s while no card was inserted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
+2
-2
@@ -35,7 +35,7 @@ static void draw() {
|
|||||||
d.setTextColor(TFT_WHITE);
|
d.setTextColor(TFT_WHITE);
|
||||||
|
|
||||||
const auto& b = battery->estimator();
|
const auto& b = battery->estimator();
|
||||||
const auto& s = storage->state();
|
const auto s = storage->state();
|
||||||
int y = 16;
|
int y = 16;
|
||||||
auto line = [&](const String& text) {
|
auto line = [&](const String& text) {
|
||||||
d.drawString(text, 4, y);
|
d.drawString(text, 4, y);
|
||||||
@@ -122,7 +122,7 @@ void loop() {
|
|||||||
|
|
||||||
if (millis() - lastLog > 5000) {
|
if (millis() - lastLog > 5000) {
|
||||||
lastLog = millis();
|
lastLog = millis();
|
||||||
const auto& s = storage->state();
|
const auto s = storage->state();
|
||||||
Serial.printf("status: batt %d%% %dmV raw %d | sd %s %d%% total %lluMB | heap %u min %u | screen %d\n",
|
Serial.printf("status: batt %d%% %dmV raw %d | sd %s %d%% total %lluMB | heap %u min %u | screen %d\n",
|
||||||
battery->estimator().percent(), battery->estimator().millivolts(),
|
battery->estimator().percent(), battery->estimator().millivolts(),
|
||||||
battery->lastRawMillivolts(), s.present ? "yes" : "no", s.usedPercent,
|
battery->lastRawMillivolts(), s.present ? "yes" : "no", s.usedPercent,
|
||||||
|
|||||||
@@ -7,11 +7,40 @@
|
|||||||
|
|
||||||
namespace roro {
|
namespace roro {
|
||||||
|
|
||||||
|
void StorageService::start() {
|
||||||
|
if (task_) return;
|
||||||
|
lock_ = xSemaphoreCreateMutex();
|
||||||
|
xTaskCreate(taskEntry, "storage", 4096, this, 1, &task_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StorageService::stop() {
|
||||||
|
if (!task_) return;
|
||||||
|
vTaskDelete(task_);
|
||||||
|
task_ = nullptr;
|
||||||
|
if (mounted_) SD.end();
|
||||||
|
mounted_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
StorageState StorageService::state() const {
|
||||||
|
xSemaphoreTake(lock_, portMAX_DELAY);
|
||||||
|
StorageState copy = monitor_.state();
|
||||||
|
xSemaphoreGive(lock_);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StorageService::taskEntry(void* self) {
|
||||||
|
auto* service = static_cast<StorageService*>(self);
|
||||||
|
for (;;) {
|
||||||
|
service->poll();
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(kPollMs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool StorageService::mount() {
|
bool StorageService::mount() {
|
||||||
return SD.begin(pins::kSdCs, sharedSpi(), 20000000, "/sd", 5, false);
|
return SD.begin(pins::kSdCs, sharedSpi(), 20000000, "/sd", 5, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageService::tick(uint32_t) {
|
void StorageService::poll() {
|
||||||
if (mounted_) {
|
if (mounted_) {
|
||||||
// Detect removal: the root can no longer be opened.
|
// Detect removal: the root can no longer be opened.
|
||||||
File root = SD.open("/");
|
File root = SD.open("/");
|
||||||
@@ -22,15 +51,13 @@ void StorageService::tick(uint32_t) {
|
|||||||
}
|
}
|
||||||
if (!mounted_) mounted_ = mount();
|
if (!mounted_) mounted_ = mount();
|
||||||
|
|
||||||
if (mounted_)
|
bool present = mounted_;
|
||||||
monitor_.update(true, SD.totalBytes(), SD.usedBytes());
|
uint64_t total = present ? SD.totalBytes() : 0;
|
||||||
else
|
uint64_t used = present ? SD.usedBytes() : 0;
|
||||||
monitor_.update(false, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StorageService::stop() {
|
xSemaphoreTake(lock_, portMAX_DELAY);
|
||||||
if (mounted_) SD.end();
|
monitor_.update(present, total, used);
|
||||||
mounted_ = false;
|
xSemaphoreGive(lock_);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace roro
|
} // namespace roro
|
||||||
|
|||||||
@@ -1,25 +1,34 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/semphr.h>
|
||||||
|
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "storage_monitor.h"
|
#include "storage_monitor.h"
|
||||||
|
|
||||||
namespace roro {
|
namespace roro {
|
||||||
|
|
||||||
// Mounts the microSD card (retrying while absent) and feeds its usage to the StorageMonitor.
|
// Mounts the microSD card (retrying while absent) and feeds its usage to the StorageMonitor.
|
||||||
|
// Runs on its own task: a failed mount blocks for seconds and must never stall the UI.
|
||||||
class StorageService : public Service {
|
class StorageService : public Service {
|
||||||
public:
|
public:
|
||||||
explicit StorageService(EventBus& bus) : monitor_(bus) {}
|
explicit StorageService(EventBus& bus) : monitor_(bus) {}
|
||||||
const char* name() const override { return "storage"; }
|
const char* name() const override { return "storage"; }
|
||||||
uint32_t tickIntervalMs() const override { return 15000; }
|
void start() override;
|
||||||
void tick(uint32_t nowMs) override;
|
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
|
||||||
const StorageState& state() const { return monitor_.state(); }
|
StorageState state() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr uint32_t kPollMs = 15000;
|
||||||
|
static void taskEntry(void* self);
|
||||||
|
void poll();
|
||||||
bool mount();
|
bool mount();
|
||||||
|
|
||||||
StorageMonitor monitor_;
|
StorageMonitor monitor_;
|
||||||
bool mounted_ = false;
|
bool mounted_ = false;
|
||||||
|
TaskHandle_t task_ = nullptr;
|
||||||
|
mutable SemaphoreHandle_t lock_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace roro
|
} // namespace roro
|
||||||
|
|||||||
Reference in New Issue
Block a user