Skip to content

Commit c45a28e

Browse files
initial commit after moving
1 parent f69d970 commit c45a28e

21 files changed

+2671
-0
lines changed

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Unified Storage Library
2+
3+
The UnifiedStorage library provides a unified interface to access different types of storage, including internal storage, SD cards, and USB mass storage devices. It simplifies the handling of files and directories across multiple storage mediums on Portenta, Opta, and some Nicla boards.
4+
5+
6+
## Examples
7+
* **examples/basic** - this example is concerned with reading/writing and seeking
8+
* **examples/advanced** - this example is concerned with more advanced features like creating folders, traversing folder sturctures and moving/copying from one storage medium to another
9+
* **examples/opta_logger** - this is more of a real life usecase, where this library is used on an Arduino Opta to log sensor data to a file on the internal storage and check if a USB Mass Storage deviece is inserted. If it is detected it will backup the information on the internal storage, only copying the bytes that are new since the last update.
10+
11+
## Instructions
12+
1. Download and install this library
13+
2. Check compatibility with your platform
14+
3. Make sure your drives are FAT32 formatted (for now, LittleFS support will be added later)
15+
4. To use internal storage, you need to make sure it is partitioned and formatted correctly
16+
* You can do that by flashing the `QSPIFormat` example that can be found in the `STM32H747_System` folder (For Portenta H7/Opta/Giga) or in the `Storage` folder for Portenta C33
17+
* Open the serial monitor and select answer with "Y" when this appears "Do you want to use partition scheme 1? Y/[n]"
18+
* Reboot the board
19+
20+
21+
## Features
22+
* Access files and directories on internal storage, SD cards, and USB mass storage devices.
23+
* Read and write data to files.
24+
* Create, remove, and rename files and directories.
25+
* Copy and move files and directories.
26+
* List files and subfolders in a directory.
27+
* Manipulate folders from one storage medium to another.
28+
29+
## Compatibility
30+
31+
This library is compatible with STM32 and Renesas based Arduino boards. The availability of storage mediums depends on the hardware interfaces.
32+
33+
* Portenta Machine Control: USB and Internal QSPI Flash
34+
* Portenta H7 + Portenta Breakout: USB, SD, and QSPI
35+
* Portenta H7 + Vision Shield: SD and QSPI
36+
* Arduino Giga R1: USB and QSPI
37+
* Arduino Opta: USB and QSPI
38+
* Portenta C33 + Portenta Breakout: USB, SD, and QSPI
39+
* Portenta C33 + Vision Shield: SD and QSPI

docs/README.md

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
2+
3+
### Use QSPI Flash, SD cards, and USB mass storage devices
4+
This library allows you to easily switch between different storage mediums on supported boards, check the "Compatibility" section for more details about what storage medium is supported on what board.
5+
6+
To initialise the storage medium you need to create a `UnifiedStorage` object, and to mount it you need to call it's `begin()` method:
7+
8+
```c
9+
UnifiedStorage storageMedium = USBStorage(); // or
10+
// UnifiedStorage sd = SDStorage();
11+
// UnifiedStorage internal = InternalStorage();
12+
13+
void setup(){
14+
storageMedium.begin();
15+
}
16+
```
17+
You can initialize a UnifiedStorage object of each type (QSPI, SD, USB), and copy files and folders from one medium to another.
18+
19+
### Open, Write, and Read Files
20+
21+
#### Creating / Opening / Closing Files
22+
23+
You can create or open a file by providing a reference to the root folder of the storage medium or by specifying the full absolute path.
24+
25+
```cpp
26+
Folder root = storageMedium.getRootFolder();
27+
UFile document = root.createFile("file.txt", FileMode::READ);
28+
```
29+
30+
```cpp
31+
UFile document = UFile();
32+
document.open("<absolute_path>/file.txt", FileMode::READ);
33+
```
34+
35+
You can change the mode of an opened file using the `changeMode()` method.
36+
37+
```cpp
38+
document.changeMode(FileMode::WRITE);
39+
```
40+
41+
#### Reading and Writing Files
42+
43+
You can read data from a file using the `read()` method and write data to a file using the `write()` method.
44+
45+
```cpp
46+
uint8_t buffer[128];
47+
size_t bytesRead = document.read(buffer, sizeof(buffer));
48+
```
49+
50+
```cpp
51+
String data = "Hello, World!";
52+
char buffer[data.length() + 1];
53+
data.toCharArray(buffer, sizeof(buffer));
54+
size_t bytesWritten = document.write(reinterpret_cast<const uint8_t*>(buffer), data.length());
55+
```
56+
57+
Alternatively, you can read and write files using strings.
58+
59+
```cpp
60+
String content = document.readAsString();
61+
```
62+
63+
```cpp
64+
String data = "Hello, World!";
65+
size_t bytesWritten = document.write(data);
66+
```
67+
68+
### File Seeking and Availability
69+
70+
The `seek()` and `available()` methods provide functionality for file seeking and checking the available data in a file. Here's an overview of how to use these methods:
71+
72+
#### Seek to a Specific Position in the File
73+
74+
The `seek()` method allows you to move the file pointer to a specific position in the file. It takes an offset parameter, which represents the number of bytes to move from a reference position. Here's an example:
75+
76+
```cpp
77+
// Seek to the 10th byte in the file
78+
bool success = file.seek(10);
79+
if (success) {
80+
// File pointer is now at the desired position
81+
} else {
82+
// Seek operation failed
83+
}
84+
```
85+
86+
In this example, the `seek()` method is called with an offset of 10, which moves the file pointer to the 10th byte in the file. The method returns a boolean value indicating the success of the seek operation.
87+
88+
#### Check Available Data in the File
89+
90+
The `available()` method returns the number of bytes available for reading from the current file position. It can be used to determine how much data is left to be read in the file. Here's an example:
91+
92+
```cpp
93+
// Check the available data in the file
94+
int availableBytes = file.available();
95+
if (availableBytes > 0) {
96+
// There is data available to be read
97+
} else {
98+
// No more data available
99+
}
100+
```
101+
102+
In this example, the `available()` method is called to retrieve the number of available bytes in the file. If the value is greater than 0, it means there is data available to be read. Otherwise, there is no more data left in the file.
103+
104+
These methods are useful for scenarios where you need to navigate to a specific position in a file or check the availability of data before performing read operations.
105+
106+
### File Manipulation
107+
The library provides various file manipulation operations such as renaming, deleting, moving, and copying files.
108+
109+
#### Renaming a File
110+
111+
You can rename an existing file using the `rename()` method.
112+
113+
```cpp
114+
bool success = document.rename("newfile.txt");
115+
```
116+
117+
#### Deleting a File
118+
119+
You can delete a file using the `remove()` method.
120+
121+
```cpp
122+
bool success = document.remove();
123+
```
124+
125+
#### Moving a File
126+
127+
You can move a file to a different directory using the `moveTo()` method.
128+
129+
```cpp
130+
Folder destination = storageMedium.getRootFolder().createSubfolder("destination");
131+
bool success = document.moveTo(destination);
132+
```
133+
134+
#### Copying a File
135+
136+
You can copy a file to a different directory using the `copyTo()` method.
137+
138+
```cpp
139+
Folder destination = storageMedium.getRootFolder().createSubfolder("destination");
140+
bool success = document.copyTo(destination);
141+
```
142+
143+
### Folder Manipulation
144+
The library provides methods to create, rename, delete, move, copy, and list directories.
145+
146+
#### Creating a Folder
147+
148+
You can create a new directory using the `createSubfolder()` method.
149+
150+
```cpp
151+
Folder root = storageMedium.getRootFolder();
152+
Folder newFolder = root.createSubfolder("new_folder");
153+
```
154+
155+
#### Renaming a Folder
156+
157+
You can rename an existing directory using the `rename()` method.
158+
159+
```cpp
160+
bool success = newFolder.rename("renamed_folder");
161+
```
162+
163+
#### Deleting a Folder
164+
165+
You can delete a directory using the `remove()` method.
166+
167+
```cpp
168+
bool success = newFolder.remove();
169+
```
170+
171+
#### Moving a Folder
172+
173+
You can move a directory to a different location using the `moveTo()` method.
174+
175+
```cpp
176+
Folder destination = storageMedium.getRootFolder().createSubfolder("destination");
177+
bool success = newFolder.moveTo(destination);
178+
```
179+
180+
#### Copying a Folder
181+
182+
You can copy a directory to a different location using the `copyTo()` method.
183+
184+
```cpp
185+
Folder destination = storageMedium.getRootFolder().createSubfolder("destination");
186+
bool success = newFolder.copyTo(destination);
187+
```
188+
189+
#### Listing Files and Directories
190+
191+
You can get a list of files and directories in a directory using the `getFiles()` and `getFolders()` methods.
192+
193+
```cpp
194+
std::vector<UFile> files = root.getFiles();
195+
std::vector<Folder> folders = root.getFolders();
196+
```
197+
198+
#### Traversing Files and Directories
199+
200+
You can traverse through all the files and directories in a directory using a loop and the `getFiles()` and `getFolders()` methods.
201+
202+
```cpp
203+
std::vector<UFile> files = root.getFiles();
204+
for ( UFile file : files) {
205+
// Perform operations on each file
206+
}
207+
208+
std::vector<Folder> folders = root.getFolders();
209+
for (Folder folder : folders) {
210+
// Perform operations on each folder
211+
}
212+
```

examples/advanced/advanced.ino

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
This example demonstrates the usage of the "UnifiedStorage" library with USB storage and internal storage.
3+
The code includes the necessary library and defines instances of the "USBStorage" and "InternalStorage" classes.
4+
5+
In the setup function, the code initializes the serial communication and mounts the USB storage and internal storage.
6+
It also reformats the internal storage to ensure a clean file system.
7+
Then, it creates a root directory in the internal storage and creates a subdirectory and a file inside it
8+
9+
The code writes some data to the file and demonstrates file operations.
10+
It copies the file from internal storage to USB storage and moves the subdirectory from internal storage to USB storage.
11+
12+
After the file operations, the code prints the contents of both the USB storage and the internal storage.
13+
It recursively prints the directories (marked as "[D]") and files (marked as "[F]") using the "printFolderContents" function.
14+
*/
15+
16+
#include "src/UnifiedStorage.h"
17+
18+
19+
USBStorage usbStorage = USBStorage();
20+
InternalStorage internalStorage = InternalStorage();
21+
22+
23+
void printFolderContents(Folder dir, int indentation = 0) {
24+
std::vector<Folder> directories = dir.getFolders();
25+
std::vector<UFile> files = dir.getFiles();
26+
27+
// Print directories
28+
for (Folder subdir : directories) {
29+
for (int i = 0; i < indentation; i++) {
30+
Serial.print(" ");
31+
}
32+
Serial.print("[D] ");
33+
Serial.println(subdir.getPath());
34+
printFolderContents(subdir, indentation + 1);
35+
}
36+
37+
// Print files
38+
for (UFile file : files) {
39+
for (int i = 0; i < indentation; i++) {
40+
Serial.print(" ");
41+
}
42+
Serial.print("[F] ");
43+
Serial.println(file.getPath());
44+
}
45+
}
46+
47+
48+
49+
void setup() {
50+
Serial.begin(115200);
51+
while (!Serial);
52+
53+
// Mount the USB storage
54+
usbStorage.begin();
55+
Serial.println("USB storage mounted.");
56+
57+
// Mount the internal storage
58+
Serial.println("Reformatting internal storage to make sure we have a clean FS");
59+
internalStorage.reformatQSPIPartition();
60+
61+
internalStorage.begin();
62+
Serial.println("Internal storage mounted.");
63+
64+
// Create a root directory in the internal storage
65+
Folder root = internalStorage.getRootFolder();
66+
67+
// Create a subdirectory and a file inside the root directory
68+
Folder subdir = root.createSubfolder("subdir");
69+
UFile file = root.createFile("file.txt", FileMode::WRITE);
70+
71+
// Write some data to the file
72+
file.write("Hello, world!");
73+
74+
// Copy the file from internal storage to USB storage
75+
bool success = file.copyTo(usbStorage.getRootFolder());
76+
if (success) {
77+
Serial.println("File copied successfully from internal storage to USB storage.");
78+
} else {
79+
Serial.println("Failed to copy file from internal storage to USB storage.");
80+
Serial.println(getErrInfo(errno));
81+
}
82+
83+
// Move the subdirectory from internal storage to USB storage
84+
success = subdir.moveTo(usbStorage.getRootFolder());
85+
if (success) {
86+
Serial.println("Subdirectory moved successfully from internal storage to USB storage.");
87+
} else {
88+
Serial.println("Failed to move subdirectory from internal storage to USB storage.");
89+
Serial.println(getErrInfo(errno));
90+
}
91+
92+
// Print the content of the USB storage
93+
Serial.println("USB storage contents:");
94+
printFolderContents(usbStorage.getRootFolder());
95+
96+
// Print the content of the internal storage
97+
Serial.println("Internal storage contents:");
98+
printFolderContents(internalStorage.getRootFolder());
99+
}
100+
101+
void loop() {
102+
// Nothing to do here
103+
}

0 commit comments

Comments
 (0)