mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-03-31 14:15:38 +00:00
Initial commit: standalone Pyxis T-Deck firmware
Split T-Deck firmware from microReticulum examples/lxmf_tdeck/ into its own repo. microReticulum is consumed as a git submodule dependency pinned to feat/t-deck. All include paths updated from relative symlinks to bare includes resolved via library build flags. Both tdeck (NimBLE) and tdeck-bluedroid environments compile successfully. Licensed under AGPLv3. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
529
lib/universal_filesystem/UniversalFileSystem.cpp
Normal file
529
lib/universal_filesystem/UniversalFileSystem.cpp
Normal file
@@ -0,0 +1,529 @@
|
||||
#include "UniversalFileSystem.h"
|
||||
|
||||
#include <Utilities/OS.h>
|
||||
#include <Log.h>
|
||||
|
||||
#ifdef ARDUINO
|
||||
void UniversalFileSystem::listDir(const char* dir) {
|
||||
Serial.print("DIR: ");
|
||||
Serial.println(dir);
|
||||
#ifdef BOARD_ESP32
|
||||
File root = SPIFFS.open(dir);
|
||||
if (!root) {
|
||||
Serial.println("Failed to opend directory");
|
||||
return;
|
||||
}
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (file.isDirectory()) {
|
||||
Serial.print(" DIR: ");
|
||||
}
|
||||
else {
|
||||
Serial.print(" FILE: ");
|
||||
}
|
||||
Serial.println(file.name());
|
||||
file.close();
|
||||
file = root.openNextFile();
|
||||
}
|
||||
root.close();
|
||||
#elif BOARD_NRF52
|
||||
File root = InternalFS.open(dir);
|
||||
if (!root) {
|
||||
Serial.println("Failed to opend directory");
|
||||
return;
|
||||
}
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (file.isDirectory()) {
|
||||
Serial.print(" DIR: ");
|
||||
}
|
||||
else {
|
||||
Serial.print(" FILE: ");
|
||||
}
|
||||
Serial.println(file.name());
|
||||
file.close();
|
||||
file = root.openNextFile();
|
||||
}
|
||||
root.close();
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
void UniversalFileSystem::listDir(const char* dir) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/*virtual*/ bool UniversalFileSystem::init() {
|
||||
TRACE("UniversalFileSystem initializing...");
|
||||
|
||||
#ifdef ARDUINO
|
||||
#ifdef BOARD_ESP32
|
||||
// Setup FileSystem
|
||||
INFO("SPIFFS mounting FileSystem");
|
||||
if (!SPIFFS.begin(true)){
|
||||
ERROR("SPIFFS FileSystem mount failed");
|
||||
return false;
|
||||
}
|
||||
uint32_t size = SPIFFS.totalBytes() / (1024 * 1024);
|
||||
Serial.print("size: ");
|
||||
Serial.print(size);
|
||||
Serial.println(" MB");
|
||||
uint32_t used = SPIFFS.usedBytes() / (1024 * 1024);
|
||||
Serial.print("used: ");
|
||||
Serial.print(used);
|
||||
Serial.println(" MB");
|
||||
// ensure FileSystem is writable and format if not
|
||||
RNS::Bytes test("test");
|
||||
size_t wrote = write_file("/test", test);
|
||||
INFO("SPIFFS write test: wrote " + std::to_string(wrote) + " bytes");
|
||||
if (wrote < 4) {
|
||||
WARNING("SPIFFS FileSystem is being formatted, please wait...");
|
||||
SPIFFS.format();
|
||||
}
|
||||
else {
|
||||
remove_file("/test");
|
||||
INFO("SPIFFS FileSystem write test passed");
|
||||
}
|
||||
DEBUG("SPIFFS FileSystem is ready");
|
||||
#elif BOARD_NRF52
|
||||
// Initialize Internal File System
|
||||
INFO("InternalFS mounting FileSystem");
|
||||
InternalFS.begin();
|
||||
INFO("InternalFS FileSystem is ready");
|
||||
#endif
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
/*virtual*/ bool UniversalFileSystem::file_exists(const char* file_path) {
|
||||
#ifdef ARDUINO
|
||||
#ifdef BOARD_ESP32
|
||||
File file = SPIFFS.open(file_path, FILE_READ);
|
||||
if (file) {
|
||||
#elif BOARD_NRF52
|
||||
File file(InternalFS);
|
||||
if (file.open(file_path, FILE_O_READ)) {
|
||||
#else
|
||||
if (false) {
|
||||
#endif
|
||||
#else
|
||||
// Native
|
||||
FILE* file = fopen(file_path, "r");
|
||||
if (file != nullptr) {
|
||||
#endif
|
||||
//TRACE("file_exists: file exists, closing file");
|
||||
#ifdef ARDUINO
|
||||
#ifdef BOARD_ESP32
|
||||
file.close();
|
||||
#elif BOARD_NRF52
|
||||
file.close();
|
||||
#endif
|
||||
#else
|
||||
// Native
|
||||
fclose(file);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
ERROR("file_exists: failed to open file " + std::string(file_path));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*virtual*/ size_t UniversalFileSystem::read_file(const char* file_path, RNS::Bytes& data) {
|
||||
size_t read = 0;
|
||||
#ifdef ARDUINO
|
||||
#ifdef BOARD_ESP32
|
||||
File file = SPIFFS.open(file_path, FILE_READ);
|
||||
if (file) {
|
||||
size_t size = file.size();
|
||||
read = file.readBytes((char*)data.writable(size), size);
|
||||
#elif BOARD_NRF52
|
||||
//File file(InternalFS);
|
||||
//if (file.open(file_path, FILE_O_READ)) {
|
||||
File file = InternalFS.open(file_path, FILE_O_READ);
|
||||
if (file) {
|
||||
size_t size = file.size();
|
||||
read = file.readBytes((char*)data.writable(size), size);
|
||||
#else
|
||||
if (false) {
|
||||
size_t size = 0;
|
||||
#endif
|
||||
#else
|
||||
// Native
|
||||
FILE* file = fopen(file_path, "r");
|
||||
if (file != nullptr) {
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t size = ftell(file);
|
||||
rewind(file);
|
||||
//size_t read = fread(data.writable(size), size, 1, file);
|
||||
read = fread(data.writable(size), 1, size, file);
|
||||
#endif
|
||||
TRACE("read_file: read " + std::to_string(read) + " bytes from file " + std::string(file_path));
|
||||
if (read != size) {
|
||||
ERROR("read_file: failed to read file " + std::string(file_path));
|
||||
data.clear();
|
||||
}
|
||||
//TRACE("read_file: closing input file");
|
||||
#ifdef ARDUINO
|
||||
#ifdef BOARD_ESP32
|
||||
file.close();
|
||||
#elif BOARD_NRF52
|
||||
file.close();
|
||||
#endif
|
||||
#else
|
||||
// Native
|
||||
fclose(file);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
ERROR("read_file: failed to open input file " + std::string(file_path));
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
/*virtual*/ size_t UniversalFileSystem::write_file(const char* file_path, const RNS::Bytes& data) {
|
||||
// CBA TODO Replace remove with working truncation
|
||||
remove_file(file_path);
|
||||
size_t wrote = 0;
|
||||
#ifdef ARDUINO
|
||||
#ifdef BOARD_ESP32
|
||||
File file = SPIFFS.open(file_path, FILE_WRITE);
|
||||
if (file) {
|
||||
wrote = file.write(data.data(), data.size());
|
||||
#elif BOARD_NRF52
|
||||
File file(InternalFS);
|
||||
if (file.open(file_path, FILE_O_WRITE)) {
|
||||
wrote = file.write(data.data(), data.size());
|
||||
#else
|
||||
if (false) {
|
||||
#endif
|
||||
#else
|
||||
// Native
|
||||
FILE* file = fopen(file_path, "w");
|
||||
if (file != nullptr) {
|
||||
//size_t wrote = fwrite(data.data(), data.size(), 1, file);
|
||||
wrote = fwrite(data.data(), 1, data.size(), file);
|
||||
#endif
|
||||
TRACE("write_file: wrote " + std::to_string(wrote) + " bytes to file " + std::string(file_path));
|
||||
if (wrote < data.size()) {
|
||||
WARNING("write_file: not all data was written to file " + std::string(file_path));
|
||||
}
|
||||
//TRACE("write_file: closing output file");
|
||||
#ifdef ARDUINO
|
||||
#ifdef BOARD_ESP32
|
||||
file.close();
|
||||
#elif BOARD_NRF52
|
||||
file.close();
|
||||
#endif
|
||||
#else
|
||||
// Native
|
||||
fclose(file);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
ERROR("write_file: failed to open output file " + std::string(file_path));
|
||||
}
|
||||
return wrote;
|
||||
}
|
||||
|
||||
/*virtual*/ RNS::FileStream UniversalFileSystem::open_file(const char* file_path, RNS::FileStream::MODE file_mode) {
|
||||
TRACEF("open_file: opening file %s", file_path);
|
||||
#ifdef ARDUINO
|
||||
#ifdef BOARD_ESP32
|
||||
const char* mode;
|
||||
if (file_mode == RNS::FileStream::MODE_READ) {
|
||||
mode = FILE_READ;
|
||||
}
|
||||
else if (file_mode == RNS::FileStream::MODE_WRITE) {
|
||||
mode = FILE_WRITE;
|
||||
}
|
||||
else if (file_mode == RNS::FileStream::MODE_APPEND) {
|
||||
mode = FILE_APPEND;
|
||||
}
|
||||
else {
|
||||
ERRORF("open_file: unsupported mode %d", file_mode);
|
||||
return {RNS::Type::NONE};
|
||||
}
|
||||
TRACEF("open_file: opening file %s in mode %s", file_path, mode);
|
||||
//// Using copy constructor to create a File* instead of local
|
||||
//File file = SPIFFS.open(file_path, mode);
|
||||
//if (!file) {
|
||||
File* file = new File(SPIFFS.open(file_path, mode));
|
||||
if (file == nullptr || !(*file)) {
|
||||
ERRORF("open_file: failed to open output file %s", file_path);
|
||||
return {RNS::Type::NONE};
|
||||
}
|
||||
TRACEF("open_file: successfully opened file %s", file_path);
|
||||
return RNS::FileStream(new UniversalFileStream(file));
|
||||
#elif BOARD_NRF52
|
||||
//File file = File(InternalFS);
|
||||
File* file = new File(InternalFS);
|
||||
int mode;
|
||||
if (file_mode == RNS::FileStream::MODE_READ) {
|
||||
mode = FILE_O_READ;
|
||||
}
|
||||
else if (file_mode == RNS::FileStream::MODE_WRITE) {
|
||||
mode = FILE_O_WRITE;
|
||||
// CBA TODO Replace remove with working truncation
|
||||
if (InternalFS.exists(file_path)) {
|
||||
InternalFS.remove(file_path);
|
||||
}
|
||||
}
|
||||
else if (file_mode == RNS::FileStream::MODE_APPEND) {
|
||||
// CBA This is the default write mode for nrf52 littlefs
|
||||
mode = FILE_O_WRITE;
|
||||
}
|
||||
else {
|
||||
ERRORF("open_file: unsupported mode %d", file_mode);
|
||||
return {RNS::Type::NONE};
|
||||
}
|
||||
if (!file->open(file_path, mode)) {
|
||||
ERRORF("open_file: failed to open output file %s", file_path);
|
||||
return {RNS::Type::NONE};
|
||||
}
|
||||
|
||||
// Seek to beginning to overwrite (this is failing on nrf52)
|
||||
//if (file_mode == RNS::FileStream::MODE_WRITE) {
|
||||
// file->seek(0);
|
||||
// file->truncate(0);
|
||||
//}
|
||||
TRACEF("open_file: successfully opened file %s", file_path);
|
||||
return RNS::FileStream(new UniversalFileStream(file));
|
||||
#else
|
||||
#warning("unsuppoprted");
|
||||
return RNS::FileStream(RNS::Type::NONE);
|
||||
#endif
|
||||
#else // ARDUINO
|
||||
// Native
|
||||
const char* mode;
|
||||
if (file_mode == RNS::FileStream::MODE_READ) {
|
||||
mode = "r";
|
||||
}
|
||||
else if (file_mode == RNS::FileStream::MODE_WRITE) {
|
||||
mode = "w";
|
||||
}
|
||||
else if (file_mode == RNS::FileStream::MODE_APPEND) {
|
||||
mode = "a";
|
||||
}
|
||||
else {
|
||||
ERRORF("open_file: unsupported mode %d", file_mode);
|
||||
return {RNS::Type::NONE};
|
||||
}
|
||||
TRACEF("open_file: opening file %s in mode %s", file_path, mode);
|
||||
FILE* file = fopen(file_path, mode);
|
||||
if (file == nullptr) {
|
||||
ERRORF("open_file: failed to open output file %s", file_path);
|
||||
return {RNS::Type::NONE};
|
||||
}
|
||||
TRACEF("open_file: successfully opened file %s", file_path);
|
||||
return RNS::FileStream(new UniversalFileStream(file));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*virtual*/ bool UniversalFileSystem::remove_file(const char* file_path) {
|
||||
#ifdef ARDUINO
|
||||
#ifdef BOARD_ESP32
|
||||
return SPIFFS.remove(file_path);
|
||||
#elif BOARD_NRF52
|
||||
return InternalFS.remove(file_path);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
#else
|
||||
// Native
|
||||
return (remove(file_path) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*virtual*/ bool UniversalFileSystem::rename_file(const char* from_file_path, const char* to_file_path) {
|
||||
#ifdef ARDUINO
|
||||
#ifdef BOARD_ESP32
|
||||
return SPIFFS.rename(from_file_path, to_file_path);
|
||||
#elif BOARD_NRF52
|
||||
return InternalFS.rename(from_file_path, to_file_path);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
#else
|
||||
// Native
|
||||
return (rename(from_file_path, to_file_path) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*virtua*/ bool UniversalFileSystem::directory_exists(const char* directory_path) {
|
||||
TRACE("directory_exists: checking for existence of directory " + std::string(directory_path));
|
||||
#ifdef ARDUINO
|
||||
#ifdef BOARD_ESP32
|
||||
File file = SPIFFS.open(directory_path, FILE_READ);
|
||||
if (file) {
|
||||
bool is_directory = file.isDirectory();
|
||||
file.close();
|
||||
return is_directory;
|
||||
}
|
||||
#elif BOARD_NRF52
|
||||
File file(InternalFS);
|
||||
if (file.open(directory_path, FILE_O_READ)) {
|
||||
bool is_directory = file.isDirectory();
|
||||
file.close();
|
||||
return is_directory;
|
||||
}
|
||||
#else
|
||||
if (false) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
// Native
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*virtual*/ bool UniversalFileSystem::create_directory(const char* directory_path) {
|
||||
#ifdef ARDUINO
|
||||
#ifdef BOARD_ESP32
|
||||
// SPIFFS is a flat filesystem - directories are just part of the file path.
|
||||
// mkdir() may fail or be a no-op, but files can still be written with the full path.
|
||||
// Try to create but don't fail if it doesn't work.
|
||||
SPIFFS.mkdir(directory_path);
|
||||
DEBUG("create_directory: SPIFFS mkdir attempted for " + std::string(directory_path));
|
||||
return true;
|
||||
#elif BOARD_NRF52
|
||||
if (!InternalFS.mkdir(directory_path)) {
|
||||
ERROR("create_directory: failed to create directorty " + std::string(directory_path));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
#else
|
||||
// Native
|
||||
struct stat st = {0};
|
||||
if (stat(directory_path, &st) == 0) {
|
||||
return true;
|
||||
}
|
||||
return (mkdir(directory_path, 0700) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*virtua*/ bool UniversalFileSystem::remove_directory(const char* directory_path) {
|
||||
TRACE("remove_directory: removing directory " + std::string(directory_path));
|
||||
#ifdef ARDUINO
|
||||
#ifdef BOARD_ESP32
|
||||
//if (!LittleFS.rmdir_r(directory_path)) {
|
||||
if (!SPIFFS.rmdir(directory_path)) {
|
||||
ERROR("remove_directory: failed to remove directorty " + std::string(directory_path));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#elif BOARD_NRF52
|
||||
if (!InternalFS.rmdir_r(directory_path)) {
|
||||
ERROR("remove_directory: failed to remove directory " + std::string(directory_path));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
#else
|
||||
// Native
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*virtua*/ std::list<std::string> UniversalFileSystem::list_directory(const char* directory_path) {
|
||||
TRACE("list_directory: listing directory " + std::string(directory_path));
|
||||
std::list<std::string> files;
|
||||
#ifdef ARDUINO
|
||||
#ifdef BOARD_ESP32
|
||||
File root = SPIFFS.open(directory_path);
|
||||
#elif BOARD_NRF52
|
||||
File root = InternalFS.open(directory_path);
|
||||
#endif
|
||||
if (!root) {
|
||||
ERROR("list_directory: failed to open directory " + std::string(directory_path));
|
||||
return files;
|
||||
}
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (!file.isDirectory()) {
|
||||
char* name = (char*)file.name();
|
||||
files.push_back(name);
|
||||
}
|
||||
// CBA Following close required to avoid leaking memory
|
||||
file.close();
|
||||
file = root.openNextFile();
|
||||
}
|
||||
TRACE("list_directory: returning directory listing");
|
||||
root.close();
|
||||
return files;
|
||||
#else
|
||||
// Native
|
||||
return files;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef ARDUINO
|
||||
|
||||
#ifdef BOARD_ESP32
|
||||
|
||||
/*virtual*/ size_t UniversalFileSystem::storage_size() {
|
||||
return SPIFFS.totalBytes();
|
||||
}
|
||||
|
||||
/*virtual*/ size_t UniversalFileSystem::storage_available() {
|
||||
return (SPIFFS.totalBytes() - SPIFFS.usedBytes());
|
||||
}
|
||||
|
||||
#elif BOARD_NRF52
|
||||
|
||||
static int _countLfsBlock(void *p, lfs_block_t block){
|
||||
lfs_size_t *size = (lfs_size_t*) p;
|
||||
*size += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static lfs_ssize_t getUsedBlockCount() {
|
||||
lfs_size_t size = 0;
|
||||
lfs_traverse(InternalFS._getFS(), _countLfsBlock, &size);
|
||||
return size;
|
||||
}
|
||||
|
||||
static int totalBytes() {
|
||||
const lfs_config* config = InternalFS._getFS()->cfg;
|
||||
return config->block_size * config->block_count;
|
||||
}
|
||||
|
||||
static int usedBytes() {
|
||||
const lfs_config* config = InternalFS._getFS()->cfg;
|
||||
const int usedBlockCount = getUsedBlockCount();
|
||||
return config->block_size * usedBlockCount;
|
||||
}
|
||||
|
||||
/*virtual*/ size_t UniversalFileSystem::storage_size() {
|
||||
//return totalBytes();
|
||||
return InternalFS.totalBytes();
|
||||
}
|
||||
|
||||
/*virtual*/ size_t UniversalFileSystem::storage_available() {
|
||||
//return (totalBytes() - usedBytes());
|
||||
return (InternalFS.totalBytes() - InternalFS.usedBytes());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
/*virtual*/ size_t UniversalFileSystem::storage_size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*virtual*/ size_t UniversalFileSystem::storage_available() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user