Public Access
Storage: format the SD card as one FAT32 partition
Repartitions the whole card (f_fdisk + f_mkfs) on the storage task. Temporarily triggered from the diagnostics screen by typing FORMAT and Enter; moves to Settings > Storage in step 7. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
+10
-1
@@ -23,6 +23,7 @@ static PowerService* power;
|
|||||||
static ClockService* clockService;
|
static ClockService* clockService;
|
||||||
|
|
||||||
static String lastKey = "-";
|
static String lastKey = "-";
|
||||||
|
static String typed; // last characters typed, for the FORMAT confirmation
|
||||||
static String lastEvent = "-";
|
static String lastEvent = "-";
|
||||||
static bool dirty = true;
|
static bool dirty = true;
|
||||||
|
|
||||||
@@ -55,7 +56,7 @@ static void draw() {
|
|||||||
line("Key: " + lastKey);
|
line("Key: " + lastKey);
|
||||||
line("Event: " + lastEvent);
|
line("Event: " + lastEvent);
|
||||||
d.setTextColor(TFT_DARKGREY);
|
d.setTextColor(TFT_DARKGREY);
|
||||||
line("+/- brightness, d: dim test (5s/10s)");
|
line(storage->formatting() ? "Formatting SD card..." : "+/- bright, d: dim test, FORMAT+enter: wipe SD");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void logEvent(const String& text) {
|
static void logEvent(const String& text) {
|
||||||
@@ -103,6 +104,14 @@ static void handleKeys() {
|
|||||||
(state.enter ? " enter" : "") + (state.del ? " del" : "");
|
(state.enter ? " enter" : "") + (state.del ? " del" : "");
|
||||||
dirty = true;
|
dirty = true;
|
||||||
|
|
||||||
|
if (state.enter) {
|
||||||
|
if (typed.endsWith("FORMAT") && storage->requestFormat()) logEvent("format requested");
|
||||||
|
typed = "";
|
||||||
|
} else {
|
||||||
|
typed += word;
|
||||||
|
if (typed.length() > 16) typed.remove(0, typed.length() - 16);
|
||||||
|
}
|
||||||
|
|
||||||
int brightness = settings.getInt(Setting::Brightness);
|
int brightness = settings.getInt(Setting::Brightness);
|
||||||
if (word == "=" || word == "+") settings.setInt(Setting::Brightness, brightness + 10);
|
if (word == "=" || word == "+") settings.setInt(Setting::Brightness, brightness + 10);
|
||||||
if (word == "-") settings.setInt(Setting::Brightness, brightness - 10);
|
if (word == "-") settings.setInt(Setting::Brightness, brightness - 10);
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
#include "storage_service.h"
|
#include "storage_service.h"
|
||||||
|
|
||||||
#include <SD.h>
|
#include <SD.h>
|
||||||
|
#include <ff.h>
|
||||||
|
#include <sd_diskio.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "platform/pins.h"
|
#include "platform/pins.h"
|
||||||
#include "platform/shared_spi.h"
|
#include "platform/shared_spi.h"
|
||||||
@@ -31,11 +35,43 @@ StorageState StorageService::state() const {
|
|||||||
void StorageService::taskEntry(void* self) {
|
void StorageService::taskEntry(void* self) {
|
||||||
auto* service = static_cast<StorageService*>(self);
|
auto* service = static_cast<StorageService*>(self);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
if (service->formatRequested_) {
|
||||||
|
service->format();
|
||||||
|
service->formatRequested_ = false;
|
||||||
|
}
|
||||||
service->poll();
|
service->poll();
|
||||||
vTaskDelay(pdMS_TO_TICKS(kPollMs));
|
// Sleep until the next poll, or until a format request wakes us early.
|
||||||
|
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(kPollMs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool StorageService::requestFormat() {
|
||||||
|
if (formatRequested_ || !task_) return false;
|
||||||
|
formatRequested_ = true;
|
||||||
|
xTaskNotifyGive(task_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StorageService::format() {
|
||||||
|
if (mounted_) SD.end();
|
||||||
|
mounted_ = false;
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
uint8_t pdrv = sdcard_init(pins::kSdCs, &sharedSpi(), 20000000);
|
||||||
|
if (pdrv != 0xFF) {
|
||||||
|
constexpr size_t kWorkSize = 4096; // FF_MAX_SS
|
||||||
|
std::unique_ptr<uint8_t[]> work(new uint8_t[kWorkSize]);
|
||||||
|
LBA_t partitions[] = {100, 0}; // one partition, 100 % of the card
|
||||||
|
char drive[3] = {static_cast<char>('0' + pdrv), ':', 0};
|
||||||
|
MKFS_PARM options = {FM_FAT32, 0, 0, 0, 0};
|
||||||
|
ok = f_fdisk(pdrv, partitions, work.get()) == FR_OK &&
|
||||||
|
f_mkfs(drive, &options, work.get(), kWorkSize) == FR_OK;
|
||||||
|
sdcard_uninit(pdrv);
|
||||||
|
}
|
||||||
|
bus_.publish(Event::withText(EventType::Notification, ok ? "SD card formatted" : "SD card format failed",
|
||||||
|
static_cast<int32_t>(ok ? NotificationLevel::Info : NotificationLevel::Warning)));
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,21 +12,29 @@ namespace roro {
|
|||||||
// Runs on its own task: a failed mount blocks for seconds and must never stall the UI.
|
// 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), bus_(bus) {}
|
||||||
const char* name() const override { return "storage"; }
|
const char* name() const override { return "storage"; }
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
|
||||||
StorageState state() const;
|
StorageState state() const;
|
||||||
|
|
||||||
|
// Erases the whole card: one partition spanning the card, formatted FAT32. Runs on the storage
|
||||||
|
// task; the result arrives as a Notification. Returns false if a format is already pending.
|
||||||
|
bool requestFormat();
|
||||||
|
bool formatting() const { return formatRequested_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint32_t kPollMs = 15000;
|
static constexpr uint32_t kPollMs = 15000;
|
||||||
static void taskEntry(void* self);
|
static void taskEntry(void* self);
|
||||||
void poll();
|
void poll();
|
||||||
bool mount();
|
bool mount();
|
||||||
|
void format();
|
||||||
|
|
||||||
StorageMonitor monitor_;
|
StorageMonitor monitor_;
|
||||||
bool mounted_ = false;
|
bool mounted_ = false;
|
||||||
|
EventBus& bus_;
|
||||||
|
volatile bool formatRequested_ = false;
|
||||||
TaskHandle_t task_ = nullptr;
|
TaskHandle_t task_ = nullptr;
|
||||||
mutable SemaphoreHandle_t lock_ = nullptr;
|
mutable SemaphoreHandle_t lock_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user