From 6ca0eeb31325c3c5e353d16db8b0c15cfb8926ac Mon Sep 17 00:00:00 2001 From: Timo Kokkonen Date: Sun, 21 Jan 2024 18:02:15 -0800 Subject: [PATCH] New command: SYS:LFS? (#70) * Add new command: SYS:LFS? * Update logging. * Update README. --- README.md | 7 ++- commands.md | 15 +++++++ src/command.c | 21 +++++++++ src/fanpico.c | 9 ++-- src/fanpico.h | 3 ++ src/flash.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 165 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3b5d834..39bd4f1 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,8 @@ Fanpico is a smart PWM (PC) fan controller based around [Raspberry Pi Pico](http * Configuration stored on the device itself (in the flash memory). * SCPI "like" programming interface (see [Command Reference](commands.md)) * Monitor each fan and motherboard output signals as well as temperatures. -* WiFi support if opting to mount Pico W on the board. This turns fanpico to "IoT" device with basic web interface. -* MQTT client (with TLS support) for loggin data and to receive commands (see [Wiki](https://github.com/tjko/fanpico/wiki/FanPico-MQTT-Tutorial) for more information). +* [WiFi support](https://github.com/tjko/fanpico/wiki/FanPico-Web-Interface) if opting to mount Pico W on the board. This turns fanpico into small "IoT" device with basic web interface. +* [MQTT client](https://github.com/tjko/fanpico/wiki/FanPico-MQTT-Tutorial) with TLS support for logging data and to receive commands. ### Interfaces @@ -108,6 +108,9 @@ To help design and test Fanpico couple other projects were born: * [Tiny PicoProbe](https://github.com/tjko/tiny-picoprobe/) - tiny PicoProbe implementation. * [Fan Test Adapter](https://github.com/tjko/fan-test-adapter/) - adapter to help simulate motherboard fan outputs. +Projects based on FanPico firmware: +* [BrickPico](https://github.com/tjko/brickpico/) - (LEGO) LED Light Kit Controller. + ### Models (PCB designs) Currently following models are available: diff --git a/commands.md b/commands.md index ed7652e..3c85a95 100644 --- a/commands.md +++ b/commands.md @@ -107,6 +107,7 @@ Fanpico supports following commands: * [SYStem:FLASH?](#systemflash) * [SYStem:LED](#systemled) * [SYStem:LED?](#systemled-1) +* [SYStem:LFS?](#systemlfs) * [SYStem:MBFANS?](#systemmbfans) * [SYStem:MEM](#systemmem) * [SYStem:MEM?](#systemmem-1) @@ -1664,6 +1665,20 @@ SYS:LED? ``` +#### SYStem:LFS? +Display information about the LittleFS filesystem in the flash memory. + +Example: +``` +SYS:LFS? +Filesystem size: 262144 +Filesystem used: 24576 +Filesystem free: 237568 +Number of files: 3 +Number of subdirectories: 0 +``` + + #### SYStem:MBFANS? Display number of MBFAN input ports available. diff --git a/src/command.c b/src/command.c index 8f02285..fa5c19f 100644 --- a/src/command.c +++ b/src/command.c @@ -2234,6 +2234,26 @@ int cmd_name(const char *cmd, const char *args, int query, char *prev_cmd) } +int cmd_littlefs(const char *cmd, const char *args, int query, char *prev_cmd) +{ + size_t size, free, used, files, dirs; + + if (!query) + return 1; + if (flash_get_fs_info(&size, &free, &files, &dirs, NULL) < 0) + return 2; + + used = size - free; + printf("Filesystem size: %u\n", size); + printf("Filesystem used: %u\n", used); + printf("Filesystem free: %u\n", free); + printf("Number of files: %u\n", files); + printf("Number of subdirectories: %u\n", dirs); + + return 0; +} + + int cmd_flash(const char *cmd, const char *args, int query, char *prev_cmd) { if (!query) @@ -2406,6 +2426,7 @@ const struct cmd_t system_commands[] = { { "FANS", 4, NULL, cmd_fans }, { "FLASH", 5, NULL, cmd_flash }, { "LED", 3, NULL, cmd_led }, + { "LFS", 3, NULL, cmd_littlefs }, { "LOG", 3, NULL, cmd_log_level }, { "MBFANS", 6, NULL, cmd_mbfans }, { "MEMory", 3, NULL, cmd_memory }, diff --git a/src/fanpico.c b/src/fanpico.c index 06fa962..071b6ba 100644 --- a/src/fanpico.c +++ b/src/fanpico.c @@ -324,7 +324,7 @@ void core1_main() delta = absolute_time_diff_us(t_last, t_now); t_last = t_now; - if (delta > max_delta || delta > 1000000) { + if (delta > max_delta) { max_delta = delta; log_msg(LOG_INFO, "core1: max_loop_time=%lld", max_delta); } @@ -391,7 +391,7 @@ void core1_main() memcpy(config, cfg, sizeof(*config)); mutex_exit(config_mutex); } else { - log_msg(LOG_INFO, "failed to get config_mutex"); + log_msg(LOG_DEBUG, "failed to get config_mutex"); } } if (time_passed(&t_state, 500)) { @@ -400,7 +400,7 @@ void core1_main() memcpy(&transfer_state, state, sizeof(transfer_state)); mutex_exit(state_mutex); } else { - log_msg(LOG_INFO, "failed to get state_mutex"); + log_msg(LOG_DEBUG, "failed to get state_mutex"); } } @@ -450,7 +450,7 @@ int main() delta = absolute_time_diff_us(t_last, t_now); t_last = t_now; - if (delta > max_delta || delta > 1000000) { + if (delta > max_delta) { max_delta = delta; log_msg(LOG_INFO, "core0: max_loop_time=%lld", max_delta); } @@ -484,7 +484,6 @@ int main() /* Update display every 1000ms */ if (time_passed(&t_display, 1000)) { - log_msg(LOG_DEBUG, "Update display"); update_system_state(); display_status(fanpico_state, cfg); } diff --git a/src/fanpico.h b/src/fanpico.h index 2c8f7cf..c4049bc 100644 --- a/src/fanpico.h +++ b/src/fanpico.h @@ -320,6 +320,9 @@ void oled_display_message(int rows, const char **text_lines); int flash_read_file(char **bufptr, uint32_t *sizeptr, const char *filename, int init_flash); int flash_write_file(const char *buf, uint32_t size, const char *filename); int flash_delete_file(const char *filename); +int flash_get_fs_info(size_t *size, size_t *free, size_t *files, + size_t *directories, size_t *filesizetotal); + /* network.c */ void network_init(); diff --git a/src/flash.c b/src/flash.c index a37895a..7f43f35 100644 --- a/src/flash.c +++ b/src/flash.c @@ -28,6 +28,8 @@ #include "fanpico.h" +extern struct lfs_config pico_cfg; + int flash_read_file(char **bufptr, uint32_t *sizeptr, const char *filename, int init_flash) { @@ -166,3 +168,118 @@ int flash_delete_file(const char *filename) return ret; } + +int littlefs_scan_dir(const char *path, size_t *files, size_t *dirs, size_t *used) +{ + lfs_dir_t dir; + struct lfs_info info; + char *dirname = NULL; + char separator[2] = "/"; + int res; + size_t f_count = 0; + size_t d_count = 0; + size_t total = 0; + size_t path_len; + + if (!path) + return -1; + + /* Check if path ends with "/"... */ + path_len = strnlen(path, LFS_NAME_MAX); + if (path_len > 0) { + if (path[path_len - 1] == '/') + separator[0] = 0; + } + + log_msg(LOG_DEBUG, "littlefs_scan_dir(%s)", path); + if ((res = lfs_dir_open(&dir, path)) < 0) + return res; + + while ((res = lfs_dir_read(&dir, &info) > 0)) { + if (info.type == LFS_TYPE_REG) { + f_count++; + total += info.size; + log_msg(LOG_DEBUG, "lfs: File '%s%s%s': size=%u", + path, separator, info.name, info.size); + } + else if (info.type == LFS_TYPE_DIR) { + /* Skip special directories ("." and "..") */ + if (info.name[0] == '.') { + if (info.name[1] == 0) + continue; + if (info.name[1] == '.' && info.name[2] == 0) + continue; + } + d_count++; + log_msg(LOG_DEBUG, "lfs: Directory '%s%s%s'", + path, separator, info.name); + if ((dirname = malloc(LFS_NAME_MAX + 1))) { + res = snprintf(dirname, LFS_NAME_MAX + 1, "%s%s%s", + path, separator, info.name); + if (res > LFS_NAME_MAX) + log_msg(LOG_WARNING, "lfs: filename truncated '%s'", dirname); + littlefs_scan_dir(dirname, files, dirs, used); + free(dirname); + } else { + log_msg(LOG_WARNING, "littlefs_scan_dir: malloc failed"); + } + } + else { + log_msg(LOG_NOTICE, "lfs: Unknown directory entry %s%s%s: type=%u, size=%u", + path, separator, info.name, info.type, info.size); + } + } + + if (files) + *files += f_count; + if (dirs) + *dirs += d_count; + if (used) + *used += total; + log_msg(LOG_DEBUG, "littlefs_scan_dir(%s): files=%u (total size=%u), dirs=%u", + path, f_count, total, d_count); + + return 0; +} + + +int flash_get_fs_info(size_t *size, size_t *free, size_t *files, + size_t *directories, size_t *filesizetotal) +{ + int res; + size_t fs_dirs = 0; + size_t fs_files = 0; + size_t fs_total = 0; + size_t used, used_blocks, fs_size; + + if (!size || !free) + return -1; + + /* Mount flash filesystem... */ + if ((res = pico_mount(false)) < 0) { + log_msg(LOG_ERR, "pico_mount() failed: %d (%s)", res, pico_errmsg(res)); + return -2; + } + + used_blocks = lfs_fs_size(); + used = used_blocks * pico_cfg.block_size; + fs_size = pico_cfg.block_count * pico_cfg.block_size; + + if ((res = littlefs_scan_dir("/", &fs_files, &fs_dirs, &fs_total)) < 0) + log_msg(LOG_ERR, "little_fs_scan_dir() failed: %d", res); + + if (size) + *size = fs_size; + if (free) + *free = fs_size - used; + if (files) + *files = fs_files; + if (directories) + *directories = fs_dirs; + if (filesizetotal) + *filesizetotal = fs_total; + + pico_unmount(); + + return 0; +}