Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 55 additions & 2 deletions include/graphics/common/SdCard.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ class ISdCard

virtual uint64_t usedBytes(void) = 0;
virtual uint64_t freeBytes(void) = 0;
// false while used/free are not known yet (e.g. a background scan on a
// co-processor has not finished); local cards always know them
virtual bool statsValid(void) { return true; }

enum StatsResult {
eStatsValid, // used/free are up to date
eStatsPending, // still being computed, ask again later
eStatsUnavailable, // card or link gone, stop asking
};
// Re-read used/free without re-detecting the card
virtual StatsResult refreshStats(void) { return eStatsValid; }
// Release the card so it can be pulled safely. False when the card cannot
// be ejected (locally mounted cards are simply not held open).
virtual bool eject(void) { return false; }
virtual uint64_t cardSize(void) = 0;
virtual bool format(void) = 0;

Expand All @@ -60,7 +74,9 @@ class ISdCard
bool updated = false;
};

#if defined(ARCH_PORTDUINO) || defined(HAS_SD_MMC)
// SENSECAP_INDICATOR takes precedence: the SD card sits behind the
// co-processor even when a generic SD define is also set
#if (defined(ARCH_PORTDUINO) || defined(HAS_SD_MMC)) && !defined(SENSECAP_INDICATOR)
class SDCard : public ISdCard
{
public:
Expand All @@ -78,7 +94,7 @@ class SDCard : public ISdCard
virtual ~SDCard(void);
};

#elif defined(HAS_SDCARD)
#elif defined(HAS_SDCARD) && !defined(SENSECAP_INDICATOR)
class SdFsCard : public ISdCard
{
public:
Expand All @@ -96,6 +112,43 @@ class SdFsCard : public ISdCard
virtual ~SdFsCard(void) {}
};

#elif defined(SENSECAP_INDICATOR)
#include "graphics/map/RemoteSDService.h"

/**
* SD card behind a co-processor, accessed through the RemoteSDService
* IRemoteFS backend. Statistics are fetched once over the link on init().
*/
class RemoteSdCard : public ISdCard
{
public:
bool init(void) override;
CardType cardType(void) override;
FatType fatType(void) override;
ErrorType errorType(void) override
{
if (info.present)
return eNoError;
// a card that is in there but has no filesystem can be formatted,
// just like a local one
return info.unformatted ? eFormatError : eSlotEmpty;
}
uint64_t usedBytes(void) override { return info.usedBytes; }
uint64_t freeBytes(void) override { return info.freeBytes; }
bool statsValid(void) override { return info.statsValid; }
StatsResult refreshStats(void) override;
bool eject(void) override;
uint64_t cardSize(void) override { return info.cardSize; }
bool format(void) override;

std::set<std::string> loadMapStyles(const char *folder) override;
std::string getUrlProvider(const char *folder, const char *style) override;
virtual ~RemoteSdCard(void) {}

private:
RemoteSdInfo info;
};

#endif

/**
Expand Down
123 changes: 123 additions & 0 deletions include/graphics/map/RemoteSDService.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#pragma once

#include "graphics/map/TileService.h"
#include "lvgl.h"
#include <set>
#include <stdint.h>
#include <string>

/**
* Statistics of the remote SD card; numeric values match the
* meshtastic.SdCardInfo interdevice protobuf enums.
*/
struct RemoteSdInfo {
bool present = false;
uint8_t cardType = 0; // 0 none, 1 MMC, 2 SD, 3 SDHC, 4 SDXC, 5 unknown
uint8_t fatType = 0; // 0 unknown, 1 FAT16, 2 FAT32, 3 exFAT
uint64_t cardSize = 0;
uint64_t usedBytes = 0;
uint64_t freeBytes = 0;
// usedBytes/freeBytes are only meaningful when true; the scan behind
// them runs in the background on the co-processor after mount
bool statsValid = false;
// a card is in the slot but carries no filesystem (present is false)
bool unformatted = false;
};

/**
* Backend transport for RemoteSDService: chunked file access on a filesystem
* that lives on another MCU (e.g. the SD card behind the SenseCAP Indicator
* RP2040). Implemented and registered by the firmware before the view loads
* the map.
*/
class IRemoteFS
{
public:
/**
* Read up to len bytes at offset. Returns false on transport error or
* missing file. *bytesRead receives the chunk size actually read,
* *fileSize the total size of the file.
*/
virtual bool readChunk(const char *path, uint32_t offset, uint8_t *buf, uint32_t len, uint32_t *bytesRead,
uint32_t *fileSize) = 0;
/**
* Sequential chunked write; create=true starts a new file (offset 0),
* create=false appends the chunk at offset == current file size.
*/
virtual bool writeChunk(const char *path, uint32_t offset, const uint8_t *buf, uint32_t len, bool create) = 0;
/**
* Delete a file. Returns false on transport error or when the file
* does not exist.
*/
virtual bool remove(const char *path) = 0;
/**
* List all entries of a directory; subdirectories carry a trailing
* slash. Returns false on transport error or when path is not a
* directory.
*/
virtual bool listDir(const char *path, std::set<std::string> &entries) = 0;
/**
* Card statistics, answered from a cache on the co-processor so the
* call stays fast. usedBytes/freeBytes carry real values once
* statsValid is true; a later call returns them when the background
* scan has finished. Returns false on transport error.
*/
virtual bool sdInfo(RemoteSdInfo &info) = 0;
/**
* Release the card so it can be pulled safely, or mount it again. The
* co-processor keeps it released until a mount is asked for.
*/
virtual bool sdEject(void) = 0;
virtual bool sdMount(void) = 0;
/**
* Put a fresh filesystem on the card, destroying what is on it.
*/
virtual bool sdFormat(void) = 0;
virtual ~IRemoteFS() {}
};

/**
* Map tile service for a remote SD card, accessed chunk-wise through an
* IRemoteFS backend. Registers an LVGL filesystem driver so the image
* decoder reads tiles like from a local drive.
*/
class RemoteSDService : public ITileService
{
public:
static void setBackend(IRemoteFS *fs);
static IRemoteFS *backend(void);

RemoteSDService();
virtual ~RemoteSDService();

bool load(const char *name, void *img) override;
bool save(const char *name, void *img, size_t len) override;

protected:
static void *fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t *drv, void *file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br);
static lv_fs_res_t fs_write(lv_fs_drv_t *drv, void *file_p, const void *buf, uint32_t btw, uint32_t *bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p);

private:
static IRemoteFS *remoteFS;

enum { CHUNK_SIZE = 4096 }; // matches FileTransfer.filedata max_size

typedef struct RemoteFile {
char path[256]; // full FAT LFN paths round-trip
uint32_t pos;
uint32_t size;
} RemoteFile;

// One chunk for all open files: the image decoder opens the same tile
// three times and reads it front to back, so they all want the same bytes.
static uint8_t cachedChunk[CHUNK_SIZE];
static char cachedPath[256];
static uint32_t cachedOffset;
static uint32_t cachedLen;
static uint32_t cachedSize;
static uint32_t chunkAt(const char *path, uint32_t pos, uint32_t *fileSize);
};
10 changes: 10 additions & 0 deletions include/graphics/view/TFT/TFTView_320x240.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,16 @@ class TFTView_320x240 : public MeshtasticView
virtual void updateTime(void);
// update SD card slot info
virtual bool updateSDCard(void);
// re-read only the card statistics (a co-processor may compute them in
// the background), polling a bounded number of times until they arrive
void refreshSDCardStats(void);
void armSDCardStatsPoll(void);
// release the card so it can be pulled safely; a tap mounts it again
void ejectSDCard(void);
#if defined(HAS_SDCARD) || defined(SENSECAP_INDICATOR)
void formatSDCardLabel(char *buf, size_t len);
#endif
uint16_t sdStatsPolls = 0;
// format SD card if invalid
virtual void formatSDCard(void);
// update time display on home screen
Expand Down
17 changes: 17 additions & 0 deletions include/input/I2CKeyboardScanner.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#pragma once

#ifdef SENSECAP_INDICATOR
#include <Wire.h> // TwoWire is a typedef on some cores, no forward declaration
#endif

class I2CKeyboardInputDriver;

/**
Expand All @@ -14,4 +18,17 @@ class I2CKeyboardScanner
I2CKeyboardScanner(void);
virtual I2CKeyboardInputDriver *scan(void);
virtual ~I2CKeyboardScanner(void) {}

#ifdef SENSECAP_INDICATOR
/**
* Override the bus used for the secondary (bus 1) keyboard scan, e.g. a
* bridged TwoWire implementation on devices whose second bus lives behind
* a co-processor. Must be set before input driver initialization; when
* unset the local Wire1 is used.
*/
static void setSecondaryBus(TwoWire *bus);

private:
static TwoWire *secondaryBus;
#endif
};
2 changes: 2 additions & 0 deletions locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ en:
'%d new messages': ~
'uptime: %02d:%02d:%02d': ~
"%s: %d GB (%s)\nUsed: %0.2f GB (%d%%)": ~
'Used: ...': ~
SD slot empty: ~
SD ejected: ~
SD invalid format: ~
SD mbr not found: ~
SD card error: ~
Expand Down
Loading
Loading