diff --git a/miosix/CMakeLists.txt b/miosix/CMakeLists.txt
index 8c1c3865617f6622785c15e3675ca5c34c3dc458..afeb594dfe807dc5d4812a5f050bdee3a0c56758 100644
--- a/miosix/CMakeLists.txt
+++ b/miosix/CMakeLists.txt
@@ -91,8 +91,9 @@ foreach(OPT_BOARD ${BOARDS})
         util/version.cpp
         util/crc16.cpp
         util/lcd44780.cpp
-        util/angel/angel_serial.cpp
         util/angel/angel.cpp
+        util/angel/angel_serial.cpp
+        util/angel/angel_fs.cpp
         ${ARCH_SRC}
     )
     add_library(Miosix::Miosix::${OPT_BOARD} ALIAS ${MIOSIX_LIBRARY})
diff --git a/miosix/util/angel/angel.cpp b/miosix/util/angel/angel.cpp
index 915f38991fc8c314f9d1db3900a7faa2035b326d..f0a5784b9fff9453def319e4354ae7b225d25a4b 100644
--- a/miosix/util/angel/angel.cpp
+++ b/miosix/util/angel/angel.cpp
@@ -24,10 +24,10 @@
 
 #include <cstring>
 
-using namespace miosix::angel;
+namespace miosix {
 
 __attribute__((target("thumb")))
-int miosix::angel::angelswi(int num2, int param2) {
+int angel::angelswi(int num2, int param2) {
     register int num asm("r0") = num2;
     register int param asm("r1") = param2;
     register int ret asm("r0");
@@ -42,96 +42,123 @@ int miosix::angel::angelswi(int num2, int param2) {
     return ret;
 }
 
-int miosix::angel::sys_clock() {
-    return angelswi(0x10, 0);
+int angel::sys_open(const char *filename, Mode mode) {
+    struct {
+        const char *filename;
+        int mode;
+        int filename_len;
+    } params;
+
+    params.filename = filename;
+    params.mode = mode;
+    params.filename_len = strlen(filename);
+    return angelswi(SYS_OPEN, reinterpret_cast<int>(&params));
 }
 
-int miosix::angel::sys_close(int handle) {
+int angel::sys_close(int handle) {
     struct {
         int handle;
     } params;
 
     params.handle = handle;
-    return angelswi(0x02, reinterpret_cast<int>(&params));
+    return angelswi(SYS_CLOSE, reinterpret_cast<int>(&params));
 }
 
-int miosix::angel::sys_errno() {
-    return angelswi(0x13, 0);
+void angel::sys_writec(char c) {
+    angelswi(SYS_WRITEC, reinterpret_cast<int>(&c));
 }
 
-int miosix::angel::sys_flen(int handle) {
+void angel::sys_write0(const char *str) {
+    angelswi(SYS_WRITE0, reinterpret_cast<int>(str));
+}
+
+int angel::sys_write(int handle, const void *buf, int len) {
     struct {
         int handle;
+        const void *buf;
+        int len;
     } params;
 
     params.handle = handle;
-    return angelswi(0x0c, reinterpret_cast<int>(&params));
+    params.buf = buf;
+    params.len = len;
+
+    return angelswi(SYS_WRITE, reinterpret_cast<int>(&params));
 }
 
-int miosix::angel::sys_istty(int handle) {
+int angel::sys_read(int handle, void *buf, int len) {
     struct {
         int handle;
+        void *buf;
+        int len;
     } params;
 
     params.handle = handle;
-    return angelswi(0x09, reinterpret_cast<int>(&params));
+    params.buf = buf;
+    params.len = len;
+
+    return angelswi(SYS_READ, reinterpret_cast<int>(&params));
+}
+
+int angel::sys_readc() {
+    return angelswi(SYS_READC, 0);
 }
 
-int miosix::angel::sys_open(const char *filename, Mode mode) {
+int angel::sys_iserror(int code) {
     struct {
-        const char *filename;
-        int mode;
-        int filename_len;
+        int code;
     } params;
 
-    params.filename = filename;
-    params.mode = mode;
-    params.filename_len = strlen(filename);
+    params.code = code;
 
-    return angelswi(0x01, reinterpret_cast<int>(&params));
+    return angelswi(SYS_ISERROR, reinterpret_cast<int>(&params));
 }
 
-int miosix::angel::sys_read(int handle, void *buf, int len) {
+int angel::sys_istty(int handle) {
     struct {
         int handle;
-        void *buf;
-        int len;
     } params;
 
     params.handle = handle;
-    params.buf = buf;
-    params.len = len;
 
-    return angelswi(0x06, reinterpret_cast<int>(&params));
+    return angelswi(SYS_ISTTY, reinterpret_cast<int>(&params));
 }
 
-int miosix::angel::sys_write(int handle, const void *buf, int len) {
+int angel::sys_seek(int handle, int pos) {
     struct {
         int handle;
-        const void *buf;
-        int len;
+        int pos;
     } params;
 
     params.handle = handle;
-    params.buf = buf;
-    params.len = len;
+    params.pos = pos;
 
-    return angelswi(0x05, reinterpret_cast<int>(&params));
+    return angelswi(SYS_SEEK, reinterpret_cast<int>(&params));
 }
 
-int miosix::angel::sys_seek(int handle, int pos) {
+int angel::sys_flen(int handle) {
     struct {
         int handle;
-        int pos;
     } params;
 
     params.handle = handle;
-    params.pos = pos;
+    return angelswi(SYS_FLEN, reinterpret_cast<int>(&params));
+}
 
-    return angelswi(0x0a, reinterpret_cast<int>(&params));
+int angel::sys_tmpnam(char *buf, int id, int len) {
+    struct {
+        char *buf;
+        int id;
+        int len;
+    } params;
+
+    params.buf = buf;
+    params.id = id;
+    params.len = len;
+    return angelswi(SYS_TMPNAM, reinterpret_cast<int>(&params));
 }
 
-int miosix::angel::sys_remove(const char *filename) {
+int angel::sys_remove(const char *filename) {
     struct {
         const char *filename;
         int filename_len;
@@ -139,11 +166,10 @@ int miosix::angel::sys_remove(const char *filename) {
 
     params.filename = filename;
     params.filename_len = strlen(filename);
-
-    return angelswi(0x0e, reinterpret_cast<int>(&params));
+    return angelswi(SYS_REMOVE, reinterpret_cast<int>(&params));
 }
 
-int miosix::angel::sys_rename(const char *old_filename, const char *new_filename) {
+int angel::sys_rename(const char *old_filename, const char *new_filename) {
     struct {
         const char *old_filename;
         int old_filename_len;
@@ -155,14 +181,88 @@ int miosix::angel::sys_rename(const char *old_filename, const char *new_filename
     params.old_filename_len = strlen(old_filename);
     params.new_filename = new_filename;
     params.new_filename_len = strlen(new_filename);
+    return angelswi(SYS_RENAME, reinterpret_cast<int>(&params));
+}
+
+int angel::sys_clock() {
+    return angelswi(SYS_CLOCK, 0);
+}
+
+int angel::sys_time() {
+    return angelswi(SYS_TIME, 0);
+}
+
+int angel::sys_system(const char *cmd) {
+    struct {
+        const char *cmd;
+        int cmd_len;
+    } params;
+
+    params.cmd = cmd;
+    params.cmd_len = strlen(cmd);
+    return angelswi(SYS_SYSTEM, reinterpret_cast<int>(&params));
+}
+
+int angel::sys_errno() {
+    return angelswi(SYS_ERRNO, 0);
+}
+
+int angel::sys_get_cmdline(char *buf, int *len) {
+    struct {
+        char *buf;
+        int len;
+    } params;
+
+    params.buf = buf;
+    params.len = *len;
 
-    return angelswi(0x0f, reinterpret_cast<int>(&params));
+    int ret = angelswi(SYS_GET_CMDLINE, reinterpret_cast<int>(&params));
+    *len = params.len;
+
+    return ret;
+}
+
+void angel::sys_heapinfo(Heapinfo *heapinfo) {
+    angelswi(SYS_HEAPINFO, reinterpret_cast<int>(heapinfo));
+}
+
+void angel::sys_exit(int reason) {
+    angelswi(SYS_EXIT, reason);
+}
+
+void angel::sys_exit_extended(int reason, int code) {
+    struct {
+        int reason;
+        int code;
+    } params;
+
+    params.reason = reason;
+    params.code = code;
+    angelswi(SYS_EXIT_EXTENDED, reinterpret_cast<int>(&params));
 }
 
-int miosix::angel::sys_open_stdout() {
+int angel::sys_elapsed(long long *ticks) {
+    struct {
+        int low;
+        int high;
+    } params;
+
+    int ret = angelswi(SYS_ELAPSED, reinterpret_cast<int>(&params));
+    *ticks = (long long)params.low | ((long long) params.high << 32);
+
+    return ret;
+}
+
+int angel::sys_tickfreq() {
+    return angelswi(SYS_TICKFREQ, 0);
+}
+
+int angel::sys_open_stdout() {
     return sys_open(":tt", MODE_W);
 }
 
-int miosix::angel::sys_open_stderr() {
+int angel::sys_open_stderr() {
     return sys_open(":tt", MODE_A);
+}
+
 }
\ No newline at end of file
diff --git a/miosix/util/angel/angel.h b/miosix/util/angel/angel.h
index 4e0988aae4a9053251985cb9a83ed296d7a18748..c59ef73a0dfcd663411282c8cfe6505c43562311 100644
--- a/miosix/util/angel/angel.h
+++ b/miosix/util/angel/angel.h
@@ -39,20 +39,67 @@ namespace miosix {
             MODE_APB = 11
         };
 
+        enum OpNum {
+            SYS_OPEN = 0x01,
+            SYS_CLOSE = 0x02,
+            SYS_WRITEC = 0x03,
+            SYS_WRITE0 = 0x04,
+            SYS_WRITE = 0x05,
+            SYS_READ = 0x06,
+            SYS_READC = 0x07,
+            SYS_ISERROR = 0x08,
+            SYS_ISTTY = 0x09,
+            SYS_SEEK = 0x0a,
+            SYS_FLEN = 0x0c,
+            SYS_TMPNAM = 0x0d,
+            SYS_REMOVE = 0x0e,
+            SYS_RENAME = 0x0f,
+            SYS_CLOCK = 0x10,
+            SYS_TIME = 0x11,
+            SYS_SYSTEM = 0x12,
+            SYS_ERRNO = 0x13,
+            SYS_GET_CMDLINE = 0x15,
+            SYS_HEAPINFO = 0x16,
+            SYS_EXIT = 0x18,
+            SYS_EXIT_EXTENDED = 0x20,
+            SYS_ELAPSED = 0x30,
+            SYS_TICKFREQ = 0x31,
+        };
+
+        struct Heapinfo {
+            void *heap_base;
+            void *heap_limit;
+            void *stack_base;
+            void *stack_limit;
+        };
+
         int angelswi(int num2, int param2);
 
-        int sys_clock();
-        int sys_close(int handle);
-        int sys_errno();
-        int sys_flen(int handle);
-        int sys_istty(int handle);
         int sys_open(const char *filename, Mode mode);
-        int sys_read(int handle, void *buf, int len);
+        int sys_close(int handle);
+        void sys_writec(char inc);
+        void sys_write0(const char *str);
         int sys_write(int handle, const void *buf, int len);
+        int sys_read(int handle, void *buf, int len);
+        int sys_readc();
+        int sys_iserror(int code);
+        int sys_istty(int handle);
         int sys_seek(int handle, int pos);
+        int sys_flen(int handle);
+        int sys_tmpnam(char *buf, int id, int len);
         int sys_remove(const char *filename);
         int sys_rename(const char *old_filename, const char *new_filename);
-    
+        int sys_clock();
+        int sys_time();
+        int sys_system(const char *cmd);
+        int sys_errno();
+        int sys_get_cmdline(char *buf, int *len);
+        void sys_heapinfo(Heapinfo *heapinfo);
+        void sys_exit(int reason);
+        void sys_exit_extended(int reason, int code);
+        int sys_elapsed(long long *ticks);
+        int sys_tickfreq();
+
         int sys_open_stdout();
         int sys_open_stderr();
     }
diff --git a/miosix/util/angel/angel_fs.cpp b/miosix/util/angel/angel_fs.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..693cd611321f6f2b4fa5f69e1ceacc5405fc989e
--- /dev/null
+++ b/miosix/util/angel/angel_fs.cpp
@@ -0,0 +1,194 @@
+/* Copyright (c) 2023 Skyward Experimental Rocketry
+ * Author: Davide Mor
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "angel_fs.h"
+
+#include <sys/fcntl.h>
+#include <errno.h>
+
+#include "filesystem/stringpart.h"
+#include "angel.h"
+
+namespace miosix {
+
+angel::Mode flagsToMode(int flags) {
+    // Try to map from flags to angel mode
+
+    // First, are we appending? This has high priority
+    if(flags & _FAPPEND)
+        // If yes, do we need to read?
+        return flags & _FREAD ? angel::MODE_APB : angel::MODE_AB;
+    
+    // Second, do we need to both read and write?
+    if(flags & _FREAD && flags & _FWRITE)
+        // If yes, do we need to truncate the file?
+        return flags & _FTRUNC ? angel::MODE_WPB : angel::MODE_RPB;
+
+    // Finally, do we need to write?
+    return angel::MODE_WB;
+}
+
+class AngelFile : public FileBase {
+public:
+    AngelFile(intrusive_ref_ptr<FilesystemBase> parent, int handle);
+    ~AngelFile();
+
+    ssize_t write(const void *data, size_t len) override;
+    ssize_t read(void *data, size_t len) override;
+    off_t lseek(off_t pos, int whence) override;
+    int fstat(struct stat *pstat) const override;
+    int ioctl(int cmd, void *arg) override;
+
+private:
+    int offset;
+    int handle;
+};
+
+AngelFile::AngelFile(intrusive_ref_ptr<FilesystemBase> parent, int handle) : 
+    FileBase(parent), 
+    offset(0), 
+    handle(handle) {}
+
+AngelFile::~AngelFile() {
+    angel::sys_close(handle);
+}
+
+ssize_t AngelFile::write(const void *data, size_t len) {
+    // TODO: Any way to detect errors?
+    int count = len - angel::sys_write(handle, data, len);
+    offset += count;
+
+    return count;
+}
+
+ssize_t AngelFile::read(void *data, size_t len) {
+    // TODO: Any way to detect errors?
+    int count = len - angel::sys_read(handle, data, len);
+    offset += count;
+
+    return count;
+}
+
+off_t AngelFile::lseek(off_t pos, int whence) {
+    int size = angel::sys_flen(handle);
+    if(size < 0)
+        return -EIO;
+
+    off_t new_offset;
+    switch(whence)
+    {
+        case SEEK_CUR:
+            new_offset = offset + pos;
+            break;
+        case SEEK_SET:
+            new_offset = pos;
+            break;
+        case SEEK_END:
+            new_offset = size - pos;
+            break;
+        default:
+            return -EINVAL;
+    }
+
+    if(new_offset < 0 || new_offset > size)
+        return -EOVERFLOW;
+
+    if(angel::sys_seek(handle, new_offset) < 0) {
+        return -angel::sys_errno();
+    } else {
+        return offset = new_offset;
+    }
+}
+
+int AngelFile::fstat(struct stat *pstat) const {
+    memset(pstat, 0, sizeof(struct stat));
+
+    // TODO: Maybe actually return this error?
+    int size = angel::sys_flen(handle);
+    if(size < 0)
+        return -EIO;
+
+    pstat->st_dev = getParent()->getFsId();
+    pstat->st_mode = S_IFREG | 0755; //-rwxr-xr-x
+    pstat->st_nlink = 1;
+    pstat->st_size = size;
+    pstat->st_blocks = (size + 511) / 512;
+
+    // This number needs to be very high because angel writes are very inefficient
+    pstat->st_blksize = 512;
+
+    return 0;
+}
+
+int AngelFile::ioctl(int cmd, void *arg) {
+    return -EINVAL;
+}
+
+AngelFs::AngelFs() {}
+
+int AngelFs::open(intrusive_ref_ptr<FileBase>& file, StringPart &name, int flags, int mode) {
+    int handle = angel::sys_open(name.c_str(), flagsToMode(flags));
+    if(handle == -1) {
+        return -angel::sys_errno();
+    }
+
+    file = intrusive_ref_ptr<AngelFile>(new AngelFile(shared_from_this(), handle));
+    return 0;
+}
+
+int AngelFs::lstat(StringPart& name, struct stat *pstat) {
+    intrusive_ref_ptr<FileBase> file;
+    int ret = open(file, name, 0, 0);
+    if(ret != 0)
+        return ret;
+
+    // We only really support files
+    return file.get()->fstat(pstat);
+}
+
+int AngelFs::unlink(StringPart& name) {
+    if(angel::sys_remove(name.c_str()) == 0) {
+        return 0;
+    } else {
+        return -angel::sys_errno();
+    }
+}
+
+int AngelFs::rename(StringPart& oldName, StringPart& newName) {
+    if(angel::sys_rename(oldName.c_str(), newName.c_str()) == 0) {
+        return 0;
+    } else {
+        return -angel::sys_errno();
+    }
+}
+
+int AngelFs::mkdir(StringPart& name, int mode) {
+    // TODO: Maybe support these using angel::sys_system?
+    return -EACCES;
+}
+
+int AngelFs::rmdir(StringPart& name) {
+    // TODO: Maybe support these using angel::sys_system?
+    return -EACCES;
+}
+
+}
\ No newline at end of file
diff --git a/miosix/util/angel/angel_fs.h b/miosix/util/angel/angel_fs.h
new file mode 100644
index 0000000000000000000000000000000000000000..41f9d859a44f63697e477f4464f2dc5a1f6cf097
--- /dev/null
+++ b/miosix/util/angel/angel_fs.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2023 Skyward Experimental Rocketry
+ * Author: Davide Mor
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#pragma once
+
+#include "filesystem/file.h"
+
+namespace miosix {
+    class AngelFs : public FilesystemBase {
+    public:
+        AngelFs();
+
+        int open(intrusive_ref_ptr<FileBase>& file, StringPart &name, int flags, int mode) override;
+        int lstat(StringPart& name, struct stat *pstat) override;
+        int unlink(StringPart& name) override;
+        int rename(StringPart& oldName, StringPart& newName) override;
+        int mkdir(StringPart& name, int mode) override;
+        int rmdir(StringPart& name) override;
+    };
+}
\ No newline at end of file
diff --git a/miosix/util/angel/angel_serial.cpp b/miosix/util/angel/angel_serial.cpp
index dcdcce77e90e25c16330aa8b86555a5485a78d10..797b3b0282bd4a0612d52d4156533ddefd9e1e55 100644
--- a/miosix/util/angel/angel_serial.cpp
+++ b/miosix/util/angel/angel_serial.cpp
@@ -27,19 +27,33 @@
 #include "angel.h"
 #include "filesystem/ioctl.h"
 
-using namespace miosix;
+namespace miosix {
 
-miosix::AngelSerial::AngelSerial() : Device(DeviceType::TTY), handle(angel::sys_open_stdout()) {}
+AngelSerial::AngelSerial(Stream stream) : 
+    Device(DeviceType::TTY), 
+    handle(stream == Stream::Stdout ? angel::sys_open_stdout() : angel::sys_open_stderr()) {}
+
+AngelSerial::~AngelSerial() {
+    if(handle != -1) {
+        angel::sys_close(handle);
+    }
+}
+
+ssize_t AngelSerial::readBlock(void *buffer, size_t size, off_t where) {
+    if(handle == -1)
+        return -EIO;
 
-ssize_t miosix::AngelSerial::readBlock(void *buffer, size_t size, off_t where) {
     return size - angel::sys_read(handle, buffer, size);
 }
 
-ssize_t miosix::AngelSerial::writeBlock(const void *buffer, size_t size, off_t where) {
+ssize_t AngelSerial::writeBlock(const void *buffer, size_t size, off_t where) {
+    if(handle == -1)
+        return -EIO;
+
     return size - angel::sys_write(handle, buffer, size);
 }
 
-int miosix::AngelSerial::ioctl(int cmd, void *arg) {
+int AngelSerial::ioctl(int cmd, void *arg) {
     if(reinterpret_cast<unsigned>(arg) & 0b11) 
         return -EFAULT; // Unaligned
     
@@ -65,3 +79,5 @@ int miosix::AngelSerial::ioctl(int cmd, void *arg) {
             return -ENOTTY; // Means the operation does not apply to this descriptor
     }
 }
+
+}
\ No newline at end of file
diff --git a/miosix/util/angel/angel_serial.h b/miosix/util/angel/angel_serial.h
index 1f2cfa90a3d272e8a109b6c6d558e35dfeccbfc9..bfe80cbc4e6e8d79ae4728bc0b2dc6edaf50927c 100644
--- a/miosix/util/angel/angel_serial.h
+++ b/miosix/util/angel/angel_serial.h
@@ -27,7 +27,13 @@
 namespace miosix {
     class AngelSerial : public miosix::Device {
     public:
-        AngelSerial();
+        enum class Stream {
+            Stdout,
+            Stderr,
+        };
+
+        AngelSerial(Stream stream);
+        ~AngelSerial();
 
         ssize_t readBlock(void *buffer, size_t size, off_t where) override;
         ssize_t writeBlock(const void *buffer, size_t size, off_t where) override;