mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-11-02 10:51:57 +00:00
fix(l2): no remount while the card is a USB drive, vector packs locked across cores, time zone list, noise sweep restore
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -280,7 +280,9 @@ static void noiseSweep(Sweep& sw, const NodePrefs* p) {
|
|||||||
sw.v[i] = (int16_t)lroundf(v[K / 2]);
|
sw.v[i] = (int16_t)lroundf(v[K / 2]);
|
||||||
}
|
}
|
||||||
radio.standby();
|
radio.standby();
|
||||||
if (p) radio_driver.setParams(p->freq, p->bw, p->sf, p->cr);
|
// Back to what the mesh uses: the repeater's own profile while relaying on
|
||||||
|
// one, not always the chat settings.
|
||||||
|
if (p) the_mesh.applyRepeaterRadio();
|
||||||
radio.startReceive();
|
radio.startReceive();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -564,7 +564,8 @@ void UITask::buildHomeClock(lv_obj_t* box) {
|
|||||||
lv_obj_set_style_pad_top(row, 12, 0);
|
lv_obj_set_style_pad_top(row, 12, 0);
|
||||||
char opts[200];
|
char opts[200];
|
||||||
int o = 0;
|
int o = 0;
|
||||||
for (int k = 0; k < telemetry::COUNT; k++) o += snprintf(opts + o, sizeof(opts) - o, "%s%s", k ? "\n" : "", telemetry::NAME[k]);
|
for (int k = 0; k < telemetry::COUNT && o < (int)sizeof(opts) - 24; k++) // 137 bytes today; the list only grows
|
||||||
|
o += snprintf(opts + o, sizeof(opts) - o, "%s%s", k ? "\n" : "", telemetry::NAME[k]);
|
||||||
for (int i = 0; i < FIELDS; i++) {
|
for (int i = 0; i < FIELDS; i++) {
|
||||||
uint8_t f = _prefs ? _prefs->dashboard_fields[i] : telemetry::NONE;
|
uint8_t f = _prefs ? _prefs->dashboard_fields[i] : telemetry::NONE;
|
||||||
if (f >= telemetry::COUNT) f = telemetry::NONE;
|
if (f >= telemetry::COUNT) f = telemetry::NONE;
|
||||||
|
|||||||
@@ -287,7 +287,12 @@ static void backlightFadeIn() { display.fadeIn(); }
|
|||||||
// is picked up.
|
// is picked up.
|
||||||
static bool s_sd_mounted = false;
|
static bool s_sd_mounted = false;
|
||||||
static uint32_t s_sd_last_try = 0;
|
static uint32_t s_sd_last_try = 0;
|
||||||
|
// Lent to a computer as a USB drive (usbDriveStart()): its driver owns the
|
||||||
|
// card's host. Mounting it again here as well -- the map, the history and
|
||||||
|
// Settings > About all ask -- would put two drivers on one card.
|
||||||
|
static bool s_sd_lent = false;
|
||||||
static bool mountStorage() {
|
static bool mountStorage() {
|
||||||
|
if (s_sd_lent) return false;
|
||||||
if (s_sd_mounted) return true;
|
if (s_sd_mounted) return true;
|
||||||
if (s_sd_last_try && millis() - s_sd_last_try < 5000) return false;
|
if (s_sd_last_try && millis() - s_sd_last_try < 5000) return false;
|
||||||
s_sd_last_try = millis() | 1;
|
s_sd_last_try = millis() | 1;
|
||||||
@@ -373,6 +378,7 @@ static bool usbDriveStart() {
|
|||||||
if (s_usb_drive) return true;
|
if (s_usb_drive) return true;
|
||||||
SD_MMC.end();
|
SD_MMC.end();
|
||||||
s_sd_mounted = false;
|
s_sd_mounted = false;
|
||||||
|
s_sd_lent = true;
|
||||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||||
host.flags = SDMMC_HOST_FLAG_1BIT;
|
host.flags = SDMMC_HOST_FLAG_1BIT;
|
||||||
sdmmc_slot_config_t slot = SDMMC_SLOT_CONFIG_DEFAULT();
|
sdmmc_slot_config_t slot = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||||
@@ -386,7 +392,7 @@ static bool usbDriveStart() {
|
|||||||
sdmmc_host_deinit();
|
sdmmc_host_deinit();
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
if (!ok) { s_sd_last_try = 0; mountStorage(); return false; }
|
if (!ok) { s_sd_lent = false; s_sd_last_try = 0; mountStorage(); return false; }
|
||||||
s_msc.vendorID("MeshCore");
|
s_msc.vendorID("MeshCore");
|
||||||
s_msc.productID("Wio L2 SD card");
|
s_msc.productID("Wio L2 SD card");
|
||||||
s_msc.productRevision("1.0");
|
s_msc.productRevision("1.0");
|
||||||
@@ -408,6 +414,7 @@ static void usbDriveStop() {
|
|||||||
delay(100);
|
delay(100);
|
||||||
sdmmc_host_deinit();
|
sdmmc_host_deinit();
|
||||||
s_usb_drive = false;
|
s_usb_drive = false;
|
||||||
|
s_sd_lent = false; // the UI restarts next (restartScreen): the card is mounted afresh
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static bool usbHost() { return false; }
|
static bool usbHost() { return false; }
|
||||||
|
|||||||
@@ -83,10 +83,9 @@ void UITask::mapRegionAction(uint8_t act) {
|
|||||||
}
|
}
|
||||||
case A_DELETE: {
|
case A_DELETE: {
|
||||||
if (!tapConfirmed(s_del_lbl, "Delete?")) return;
|
if (!tapConfirmed(s_del_lbl, "Delete?")) return;
|
||||||
char path[80];
|
char file[sizeof(p.file)];
|
||||||
snprintf(path, sizeof(path), "%s/%s", mapview::VECTOR_ROOT, p.file);
|
snprintf(file, sizeof(file), "%s", p.file); // p is the list's: read again by removePack()
|
||||||
mapview::s_vpacks.closeOpen();
|
mapview::s_vpacks.removePack(file);
|
||||||
remove(path);
|
|
||||||
mapview::s_vector.available(); // lists the packs again, forgets the data read
|
mapview::s_vector.available(); // lists the packs again, forgets the data read
|
||||||
mapview::labels::flush();
|
mapview::labels::flush();
|
||||||
mapview::s_cache.invalidate();
|
mapview::s_cache.invalidate();
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ void UITask::scopeAction(uint8_t act) {
|
|||||||
char t[40];
|
char t[40];
|
||||||
snprintf(t, sizeof(t), "Delete %s?", sl.name((uint8_t)s_scope));
|
snprintf(t, sizeof(t), "Delete %s?", sl.name((uint8_t)s_scope));
|
||||||
confirmBody(navPopupPanel(t, false),
|
confirmBody(navPopupPanel(t, false),
|
||||||
"Channels using it go back to no scope. If it is the default, * becomes the default.",
|
"Channels using it follow the default again. If it is the default, * becomes the default.",
|
||||||
LV_SYMBOL_TRASH " Delete", onScopeAction, S_DELETE_GO);
|
LV_SYMBOL_TRASH " Delete", onScopeAction, S_DELETE_GO);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -487,7 +487,9 @@ void UITask::schemaRow(lv_obj_t* card, int i) {
|
|||||||
lv_obj_add_event_cb(sw, onSchemaSwitch, LV_EVENT_VALUE_CHANGED, (void*)(uintptr_t)i);
|
lv_obj_add_event_cb(sw, onSchemaSwitch, LV_EVENT_VALUE_CHANGED, (void*)(uintptr_t)i);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
char opts[160];
|
// The longest list, the time zone, is 27 options and 167 bytes: at 160
|
||||||
|
// it lost UTC+8 .. +14, and a zone set from the app showed as UTC-12.
|
||||||
|
char opts[320];
|
||||||
int o = 0;
|
int o = 0;
|
||||||
for (uint8_t k = 0; k < st.count && o < (int)sizeof(opts) - 16; k++) {
|
for (uint8_t k = 0; k < st.count && o < (int)sizeof(opts) - 16; k++) {
|
||||||
if (k) opts[o++] = '\n';
|
if (k) opts[o++] = '\n';
|
||||||
|
|||||||
@@ -414,6 +414,7 @@ void UITask::storageFormatTap(bool go) {
|
|||||||
lv_refr_now(NULL); // shown before the loop blocks
|
lv_refr_now(NULL); // shown before the loop blocks
|
||||||
stopWalk();
|
stopWalk();
|
||||||
if (mapview::s_dl.active()) mapDownloadStop();
|
if (mapview::s_dl.active()) mapDownloadStop();
|
||||||
|
mapview::s_vpacks.closeOpen();
|
||||||
bool ok = lvport::formatCard();
|
bool ok = lvport::formatCard();
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
if (s_fmt_overlay) lv_obj_delete_async(s_fmt_overlay);
|
if (s_fmt_overlay) lv_obj_delete_async(s_fmt_overlay);
|
||||||
@@ -504,6 +505,7 @@ void UITask::usbTap(bool drive) {
|
|||||||
if (!drive || locked()) return;
|
if (!drive || locked()) return;
|
||||||
storeview::stopWalk();
|
storeview::stopWalk();
|
||||||
if (mapview::s_dl.active()) mapDownloadStop();
|
if (mapview::s_dl.active()) mapDownloadStop();
|
||||||
|
mapview::s_vpacks.closeOpen(); // a pack file kept open between reads: not across the unmount
|
||||||
if (!lvport::usbDriveStart()) { showToast("No SD card to share", 3000); return; }
|
if (!lvport::usbDriveStart()) { showToast("No SD card to share", 3000); return; }
|
||||||
usbPanel("USB drive",
|
usbPanel("USB drive",
|
||||||
"The computer has the SD card now. Eject it there (or unplug the cable) when you're done - "
|
"The computer has the SD card now. Eject it there (or unplug the cable) when you're done - "
|
||||||
|
|||||||
@@ -951,6 +951,7 @@ void UITask::fillStart(int n, int first, void (UITask::*row)(int)) {
|
|||||||
void UITask::fillStep() {
|
void UITask::fillStep() {
|
||||||
FillJob& j = s_fill[0];
|
FillJob& j = s_fill[0];
|
||||||
(this->*j.fn)(j.next++); // may queue more, behind
|
(this->*j.fn)(j.next++); // may queue more, behind
|
||||||
|
if (!s_fill_n) return; // the piece built a new screen: the queue went with the old one
|
||||||
if (j.next >= j.end) memmove(s_fill, s_fill + 1, --s_fill_n * sizeof(FillJob));
|
if (j.next >= j.end) memmove(s_fill, s_fill + 1, --s_fill_n * sizeof(FillJob));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,25 +35,75 @@ public:
|
|||||||
// start), then done() -- a pack's file stays open for the next read.
|
// start), then done() -- a pack's file stays open for the next read.
|
||||||
struct Src { FILE* f = nullptr; uint32_t len = 0; bool owned = false; };
|
struct Src { FILE* f = nullptr; uint32_t len = 0; bool owned = false; };
|
||||||
|
|
||||||
|
// The loop (labels, the region list, a scan) and the tile task on the other
|
||||||
|
// core both read packs: one shared FILE* and index. A recursive lock spans
|
||||||
|
// find() to done() and every change to the list, so one never closes or
|
||||||
|
// seeks the file under the other.
|
||||||
|
#if defined(ESP32)
|
||||||
|
VectorPacks() : _mx(xSemaphoreCreateRecursiveMutex()) {}
|
||||||
|
void lock() { if (_mx) xSemaphoreTakeRecursive(_mx, portMAX_DELAY); }
|
||||||
|
void unlock() { if (_mx) xSemaphoreGiveRecursive(_mx); }
|
||||||
|
#else
|
||||||
|
void lock() {}
|
||||||
|
void unlock() {}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Lists the packs again (the map opening, a download finished).
|
// Lists the packs again (the map opening, a download finished).
|
||||||
void scan() {
|
void scan() {
|
||||||
|
lock();
|
||||||
closeOpen();
|
closeOpen();
|
||||||
for (int i = 0; i < _n; i++) free(_p[i].index);
|
for (int i = 0; i < _n; i++) free(_p[i].index);
|
||||||
_n = 0;
|
_n = 0;
|
||||||
DIR* d = opendir(VECTOR_ROOT);
|
DIR* d = opendir(VECTOR_ROOT);
|
||||||
if (!d) return;
|
if (!d) { unlock(); return; }
|
||||||
while (struct dirent* e = readdir(d)) {
|
while (struct dirent* e = readdir(d)) {
|
||||||
size_t l = strlen(e->d_name);
|
size_t l = strlen(e->d_name);
|
||||||
if (_n >= MAX || l < 5 || l >= sizeof(_p[0].file) || strcasecmp(e->d_name + l - 4, ".vpk") != 0) continue;
|
if (_n >= MAX || l < 5 || l >= sizeof(_p[0].file) || strcasecmp(e->d_name + l - 4, ".vpk") != 0) continue;
|
||||||
if (open(_p[_n], e->d_name)) _n++;
|
if (open(_p[_n], e->d_name)) _n++;
|
||||||
}
|
}
|
||||||
closedir(d);
|
closedir(d);
|
||||||
|
unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
int count() const { return _n; }
|
int count() const { return _n; }
|
||||||
const Pack& at(int i) const { return _p[i]; }
|
const Pack& at(int i) const { return _p[i]; }
|
||||||
|
|
||||||
|
// True: `s` is ready to read and the lock is held until done(s).
|
||||||
bool find(uint8_t kind, int dz, int x, int y, Src& s) {
|
bool find(uint8_t kind, int dz, int x, int y, Src& s) {
|
||||||
|
lock();
|
||||||
|
if (findLocked(kind, dz, x, y, s)) return true;
|
||||||
|
unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void done(Src& s) {
|
||||||
|
if (s.owned && s.f) fclose(s.f);
|
||||||
|
s.f = nullptr;
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before a pack file is replaced or deleted.
|
||||||
|
void closeOpen() {
|
||||||
|
lock();
|
||||||
|
if (_open_f) fclose(_open_f);
|
||||||
|
_open_f = nullptr;
|
||||||
|
_open_i = -1;
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes a pack: closed and removed under the lock (the tile task can't
|
||||||
|
// reopen it in between), then the list read again.
|
||||||
|
void removePack(const char* file) {
|
||||||
|
lock();
|
||||||
|
closeOpen();
|
||||||
|
char path[64];
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", VECTOR_ROOT, file);
|
||||||
|
remove(path);
|
||||||
|
scan();
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool findLocked(uint8_t kind, int dz, int x, int y, Src& s) {
|
||||||
uint64_t key = keyOf(kind, dz, x, y);
|
uint64_t key = keyOf(kind, dz, x, y);
|
||||||
for (int i = 0; i < _n; i++) {
|
for (int i = 0; i < _n; i++) {
|
||||||
Pack& p = _p[i];
|
Pack& p = _p[i];
|
||||||
@@ -75,23 +125,14 @@ public:
|
|||||||
s.f = f; s.len = sz > 0 ? (uint32_t)sz : 0; s.owned = true;
|
s.f = f; s.len = sz > 0 ? (uint32_t)sz : 0; s.owned = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void done(Src& s) {
|
|
||||||
if (s.owned && s.f) fclose(s.f);
|
|
||||||
s.f = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Before a pack file is replaced or deleted.
|
|
||||||
void closeOpen() {
|
|
||||||
if (_open_f) fclose(_open_f);
|
|
||||||
_open_f = nullptr;
|
|
||||||
_open_i = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Pack _p[MAX];
|
Pack _p[MAX];
|
||||||
int _n = 0;
|
int _n = 0;
|
||||||
FILE* _open_f = nullptr; // the pack read last
|
FILE* _open_f = nullptr; // the pack read last
|
||||||
int _open_i = -1;
|
int _open_i = -1;
|
||||||
|
#if defined(ESP32)
|
||||||
|
SemaphoreHandle_t _mx;
|
||||||
|
#endif
|
||||||
static const uint32_t INDEX_IN_RAM_MAX = 2u * 1024 * 1024;
|
static const uint32_t INDEX_IN_RAM_MAX = 2u * 1024 * 1024;
|
||||||
|
|
||||||
static uint64_t keyOf(uint8_t kind, int dz, int x, int y) {
|
static uint64_t keyOf(uint8_t kind, int dz, int x, int y) {
|
||||||
|
|||||||
@@ -441,6 +441,10 @@ public:
|
|||||||
|
|
||||||
int ellipsis_width = getTextWidth(ellipsis);
|
int ellipsis_width = getTextWidth(ellipsis);
|
||||||
int str_len = strlen(temp_str);
|
int str_len = strlen(temp_str);
|
||||||
|
// Room for the ellipsis whatever the trim below leaves (a near-full
|
||||||
|
// buffer of narrow glyphs on a wide panel could keep all but one byte).
|
||||||
|
const int room = (int)sizeof(temp_str) - 1 - (int)strlen(ellipsis);
|
||||||
|
if (str_len > room) temp_str[str_len = room] = 0;
|
||||||
|
|
||||||
while (str_len > 0 && getTextWidth(temp_str) > max_width - ellipsis_width) {
|
while (str_len > 0 && getTextWidth(temp_str) > max_width - ellipsis_width) {
|
||||||
temp_str[--str_len] = 0;
|
temp_str[--str_len] = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user