diff --git a/.github/workflows/lv_sim.yml b/.github/workflows/lv_sim.yml index fc5527e..87350a8 100644 --- a/.github/workflows/lv_sim.yml +++ b/.github/workflows/lv_sim.yml @@ -64,7 +64,13 @@ jobs: cmake --build build_lv_sim - name: Upload simulator executable - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: infinisim path: build_lv_sim/infinisim + + - name: Upload littlefs-do executable + uses: actions/upload-artifact@v3 + with: + name: littlefs-do + path: build_lv_sim/littlefs-do diff --git a/CMakeLists.txt b/CMakeLists.txt index c178618..651ca3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,22 +50,64 @@ configure_file( "${CMAKE_CURRENT_BINARY_DIR}/lv_conf.h" COPYONLY) -file(GLOB_RECURSE INCLUDES "lv_drivers/*.h" "${InfiniTime_DIR}/src/libs/lvgl/src/*.h" "./*.h" ) -file(GLOB_RECURSE SOURCES "lv_drivers/*.c" "${InfiniTime_DIR}/src/libs/lvgl/src/*.c" ) +file(GLOB_RECURSE LVGL_INCLUDES "lv_drivers/*.h" "${InfiniTime_DIR}/src/libs/lvgl/src/*.h" "./*.h" ) +file(GLOB_RECURSE LVGL_SOURCES "lv_drivers/*.c" "${InfiniTime_DIR}/src/libs/lvgl/src/*.c" ) + +add_library(sim-base STATIC + # LVGL sources + ${LVGL_SOURCES} ${LVGL_INCLUDES} + # FreeRTOS + sim/FreeRTOS.h + sim/FreeRTOS.cpp + sim/task.h + sim/task.cpp + sim/timers.h + sim/timers.cpp + sim/queue.h + sim/queue.cpp + # src/FreeRTOS + sim/portmacro_cmsis.h + sim/portmacro_cmsis.cpp + # nrf + sim/libraries/log/nrf_log.h + sim/libraries/delay/nrf_delay.h + sim/libraries/delay/nrf_delay.cpp + sim/nrfx/nrfx_log.h + sim/nrfx/drivers/include/nrfx_twi.h + sim/nrfx/hal/nrf_gpio.h + sim/nrfx/hal/nrf_gpio.cpp + sim/nrfx/hal/nrfx_gpiote.h # includes hal/nrf_gpio.h + sim/nrfx/hal/nrf_rtc.h + sim/nrfx/hal/nrf_rtc.cpp + # nrf/components/libraries/timer + sim/libraries/timer/app_timer.h + sim/libraries/timer/app_timer.cpp + sim/libraries/gpiote/app_gpiote.h # includes hal/nrf_gpio.h +) +# include the generated lv_conf.h file before anything else +target_include_directories(sim-base PUBLIC "${CMAKE_CURRENT_BINARY_DIR}") # lv_conf.h +target_include_directories(sim-base PUBLIC "sim") +target_include_directories(sim-base PUBLIC "sim/libraries/log") # for nrf_log.h +target_include_directories(sim-base PUBLIC "sim/libraries/timer") # for app_timer.h +target_include_directories(sim-base PUBLIC "sim/nrfx") # for nrfx_log.h and others +target_include_directories(sim-base PUBLIC "sim/nrfx/hal") # for nrfx_log.h + +target_include_directories(sim-base PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") # lv_drv_conf.h +target_include_directories(sim-base PUBLIC "${InfiniTime_DIR}/src/libs") +target_include_directories(sim-base PUBLIC "lv_drivers") + +target_include_directories(sim-base PUBLIC "${InfiniTime_DIR}/src") # InfiniTime drivers, components and all -add_executable(infinisim main.cpp ${SOURCES} ${INCLUDES}) set(MONITOR_ZOOM 1 CACHE STRING "Scale simulator window by this factor") if(MONITOR_ZOOM MATCHES "^[0-9]\.?[0-9]*") message(STATUS "Using MONITOR_ZOOM=${MONITOR_ZOOM}") - target_compile_definitions(infinisim PRIVATE MONITOR_ZOOM=${MONITOR_ZOOM}) + target_compile_definitions(sim-base PUBLIC MONITOR_ZOOM=${MONITOR_ZOOM}) else() message(FATAL_ERROR "variable MONITOR_ZOOM=${MONITOR_ZOOM} must be a positive number") endif() -# include the generated lv_conf.h file before anything else -target_include_directories(infinisim PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") - +add_executable(infinisim main.cpp) # add simulator files target_sources(infinisim PUBLIC sim/displayapp/LittleVgl.h @@ -108,45 +150,8 @@ target_sources(infinisim PUBLIC sim/drivers/SpiNorFlash.cpp sim/heartratetask/HeartRateTask.h sim/heartratetask/HeartRateTask.cpp - # FreeRTOS - sim/FreeRTOS.h - sim/FreeRTOS.cpp - sim/task.h - sim/task.cpp - sim/timers.h - sim/timers.cpp - sim/queue.h - sim/queue.cpp - # src/FreeRTOS - sim/portmacro_cmsis.h - sim/portmacro_cmsis.cpp - # nrf - sim/libraries/log/nrf_log.h - sim/libraries/delay/nrf_delay.h - sim/libraries/delay/nrf_delay.cpp - sim/nrfx/nrfx_log.h - sim/nrfx/drivers/include/nrfx_twi.h - sim/nrfx/hal/nrf_gpio.h - sim/nrfx/hal/nrf_gpio.cpp - sim/nrfx/hal/nrfx_gpiote.h # includes hal/nrf_gpio.h - sim/nrfx/hal/nrf_rtc.h - sim/nrfx/hal/nrf_rtc.cpp - # nrf/components/libraries/timer - sim/libraries/timer/app_timer.h - sim/libraries/timer/app_timer.cpp - sim/libraries/gpiote/app_gpiote.h # includes hal/nrf_gpio.h - ) -target_include_directories(infinisim PRIVATE "sim") -target_include_directories(infinisim PRIVATE "sim/libraries/log") # for nrf_log.h -target_include_directories(infinisim PRIVATE "sim/libraries/timer") # for app_timer.h -target_include_directories(infinisim PRIVATE "sim/nrfx") # for nrfx_log.h and others -target_include_directories(infinisim PRIVATE "sim/nrfx/hal") # for nrfx_log.h - -target_compile_definitions(infinisim PRIVATE LV_CONF_INCLUDE_SIMPLE) - -target_include_directories(infinisim PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") -target_include_directories(infinisim PRIVATE "${InfiniTime_DIR}/src/libs") -target_include_directories(infinisim PRIVATE "lv_drivers") +) +target_link_libraries(infinisim PUBLIC sim-base) # add dates library if(EXISTS "${InfiniTime_DIR}/src/libs/date/includes") @@ -175,21 +180,12 @@ file(GLOB InfiniTime_WIDGETS "${InfiniTime_DIR}/src/displayapp/widgets/*.cpp" "${InfiniTime_DIR}/src/displayapp/widgets/*.h" ) -set(LITTLEFS_SRC - ${InfiniTime_DIR}/src/libs/littlefs/lfs_util.h - ${InfiniTime_DIR}/src/libs/littlefs/lfs.h - ${InfiniTime_DIR}/src/libs/littlefs/lfs_util.c - ${InfiniTime_DIR}/src/libs/littlefs/lfs.c -) target_sources(infinisim PUBLIC ${InfiniTime_SCREENS}) target_sources(infinisim PUBLIC ${InfiniTime_FONTS}) target_sources(infinisim PUBLIC ${InfiniTime_ICONS}) target_sources(infinisim PUBLIC ${InfiniTime_WIDGETS}) -target_sources(infinisim PUBLIC ${LITTLEFS_SRC}) # add files directly from InfiniTime sources -target_include_directories(infinisim PRIVATE "${InfiniTime_DIR}/src") -target_include_directories(infinisim PRIVATE "${InfiniTime_DIR}/src/libs/littlefs") target_sources(infinisim PUBLIC ${InfiniTime_DIR}/src/BootloaderVersion.h ${InfiniTime_DIR}/src/BootloaderVersion.cpp @@ -242,6 +238,16 @@ else() ) endif() +# littlefs +add_library(littlefs STATIC + ${InfiniTime_DIR}/src/libs/littlefs/lfs_util.h + ${InfiniTime_DIR}/src/libs/littlefs/lfs.h + ${InfiniTime_DIR}/src/libs/littlefs/lfs_util.c + ${InfiniTime_DIR}/src/libs/littlefs/lfs.c +) +target_include_directories(littlefs PUBLIC "${InfiniTime_DIR}/src/libs/littlefs") +target_link_libraries(infinisim PRIVATE littlefs) + # QCBOR add_library(QCBOR STATIC ${InfiniTime_DIR}/src/libs/QCBOR/src/ieee754.c @@ -305,3 +311,28 @@ endif() target_include_directories(infinisim PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/gif-h") install(TARGETS infinisim DESTINATION bin) + +# helper library to manipulate littlefs raw image +add_executable(littlefs-do + littlefs-do-main.cpp + # want to use FS.h + ${InfiniTime_DIR}/src/components/fs/FS.h + ${InfiniTime_DIR}/src/components/fs/FS.cpp + # dependencies for FS.h + sim/drivers/SpiNorFlash.h + sim/drivers/SpiNorFlash.cpp + # dependencies for SpiNorFlash.h + ${InfiniTime_DIR}/src/drivers/Spi.h + ${InfiniTime_DIR}/src/drivers/Spi.cpp + sim/drivers/SpiMaster.h + sim/drivers/SpiMaster.cpp + + ${InfiniTime_DIR}/src/components/settings/Settings.h + ${InfiniTime_DIR}/src/components/settings/Settings.cpp +) + +target_link_libraries(littlefs-do PUBLIC sim-base) +target_link_libraries(littlefs-do PUBLIC littlefs) + +target_link_libraries(littlefs-do PRIVATE SDL2::SDL2) +target_link_libraries(littlefs-do PRIVATE infinitime_fonts) diff --git a/README.md b/README.md index 2dfc037..bba73fe 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,26 @@ Using the keyboard the following events can be triggered: - `i` ... take screenshot - `I` ... start/stop Gif scren capture +## Littlefs-do helper + +To help working with the SPI-raw file the tool `littlefs-do` is provided. +The SPI-raw file emulates the persistent 4MB storage available over the SPI bus on the PineTime. + +```sh +$ ./littlefs-do --help +Usage: ./littlefs-do [options] +Commands: + -h, --help show this help message for the selected command and exit + -v, --verbose print status messages to the console + stat show information of specified file or directory + ls list available files in 'spiNorFlash.raw' file + mkdir create directory + rmdir remove directory + rm remove directory or file + cp copy files into or out of flash file + settings list settings from 'settings.h' +``` + ## Licenses This project is released under the GNU General Public License version 3 or, at your option, any later version. diff --git a/littlefs-do-main.cpp b/littlefs-do-main.cpp new file mode 100644 index 0000000..728de83 --- /dev/null +++ b/littlefs-do-main.cpp @@ -0,0 +1,661 @@ + +/** + * @file littlefs-do-main.cpp + * + */ + +/********************* + * INCLUDES + *********************/ +#define _DEFAULT_SOURCE /* needed for usleep() */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include // std::pow + +#include "components/fs/FS.h" +#include "components/settings/Settings.h" +#include "drivers/SpiNorFlash.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * VARIABLES + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +Pinetime::Drivers::SpiNorFlash spiNorFlash {"spiNorFlash.raw"}; + +Pinetime::Controllers::FS fs {spiNorFlash}; +Pinetime::Controllers::Settings settingsController {fs}; + +const char* lfs_error_to_string(int err) { + if (err == LFS_ERR_OK) return "LFS_ERR_OK"; // No error + if (err == LFS_ERR_IO) return "LFS_ERR_IO"; // Error during device operation + if (err == LFS_ERR_CORRUPT) return "LFS_ERR_CORRUPT"; // Corrupted + if (err == LFS_ERR_NOENT) return "LFS_ERR_NOENT"; // No directory entry + if (err == LFS_ERR_EXIST) return "LFS_ERR_EXIST"; // Entry already exists + if (err == LFS_ERR_NOTDIR) return "LFS_ERR_NOTDIR"; // Entry is not a dir + if (err == LFS_ERR_ISDIR) return "LFS_ERR_ISDIR"; // Entry is a dir + if (err == LFS_ERR_NOTEMPTY) return "LFS_ERR_NOTEMPTY"; // Dir is not empty + if (err == LFS_ERR_BADF) return "LFS_ERR_BADF"; // Bad file number + if (err == LFS_ERR_FBIG) return "LFS_ERR_FBIG"; // File too large + if (err == LFS_ERR_INVAL) return "LFS_ERR_INVAL"; // Invalid parameter + if (err == LFS_ERR_NOSPC) return "LFS_ERR_NOSPC"; // No space left on device + if (err == LFS_ERR_NOMEM) return "LFS_ERR_NOMEM"; // No more memory available + if (err == LFS_ERR_NOATTR) return "LFS_ERR_NOATTR"; // No data/attr available + if (err == LFS_ERR_NAMETOOLONG) return "LFS_ERR_NAMETOOLONG"; // File name too long + return "unknown"; +} + +void print_help_generic(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " [options]" << std::endl; + std::cout << "Commands:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; + std::cout << " -v, --verbose print status messages to the console" << std::endl; + std::cout << " stat show information of specified file or directory" << std::endl; + std::cout << " ls list available files in 'spiNorFlash.raw' file" << std::endl; + std::cout << " mkdir create directory" << std::endl; + std::cout << " rmdir remove directory" << std::endl; + std::cout << " rm remove directory or file" << std::endl; + std::cout << " cp copy files into or out of flash file" << std::endl; + std::cout << " settings list settings from 'settings.h'" << std::endl; +} +void print_help_stat(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " stat [options] [path]" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; + std::cout << " path path to directory or file to work on, defaults to '/'" << std::endl; +} +void print_help_ls(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " ls [options] [path]" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; + std::cout << " path path to directory or file to work on, defaults to '/'" << std::endl; +} +void print_help_mkdir(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " mkdir [options] path" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; + std::cout << " path path to directory to create" << std::endl; +} +void print_help_rmdir(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " rmdir [options] path" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; + std::cout << " path path to directory to remove" << std::endl; +} +void print_help_rm(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " rm [options] path" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; + std::cout << " path path to file or directory directory to remove" << std::endl; +} +void print_help_cp(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " cp [options] source [source2 ...] destination" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; + std::cout << std::endl; + std::cout << "if the destination starts with '/' it is assumed to copy sources from" << std::endl; + std::cout << "the host system into the raw image, otherwise the files are copied" << std::endl; + std::cout << "from the raw image to the host system provided directory." << std::endl; +} +void print_help_settings(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " settings [options]" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; +} + +int command_stat(const std::string &program_name, const std::vector &args, bool verbose) +{ + if (verbose) + std::cout << "running command 'stat'" << std::endl; + // argv: littlefs-do stat [args] + std::string path = "/"; + // check for help flag first + for (const std::string &arg : args) + { + if (arg == "-h" || arg == "--help") + { + print_help_stat(program_name); + return 0; + } + } + if (args.empty()) { + if (verbose) { + std::cout << "no path given, showing '/'" << std::endl; + } + } if (args.size() == 1) { + path = args.at(0); + } + + lfs_info info; + int ret = fs.Stat(path.c_str(), &info); + if (ret) { + std::cout << "fs.Stat returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + if (info.type == LFS_TYPE_REG) { + std::cout << "type: REG" << std::endl; + std::cout << "size: " << info.size << std::endl; + std::cout << "name: " << std::string(info.name) << std::endl; + } else if (info.type == LFS_TYPE_DIR) { + std::cout << "type: DIR" << std::endl; + std::cout << "name: " << std::string(info.name) << std::endl; + } + else { + std::cout << "unknown type: " << info.type << std::endl; + return 1; + } + return 0; +} + +int command_ls(const std::string &program_name, const std::vector &args, bool verbose) +{ + if (verbose) + std::cout << "running command 'ls'" << std::endl; + // argv: littlefs-do ls [args] + std::string path = "/"; + // check for help flag first + for (const std::string &arg : args) + { + if (arg == "-h" || arg == "--help") + { + print_help_ls(program_name); + return 0; + } + } + if (args.empty()) { + if (verbose) { + std::cout << "no path given, showing '/'" << std::endl; + } + } if (args.size() == 1) { + path = args.at(0); + } + + lfs_info info; + int ret = fs.Stat(path.c_str(), &info); + if (ret) { + std::cout << "fs.Stat returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + if (info.type == LFS_TYPE_REG) { + std::cout << "type: REG" << std::endl; + std::cout << "size: " << info.size << std::endl; + std::cout << "name: " << std::string(info.name) << std::endl; + } else if (info.type == LFS_TYPE_DIR) { + std::cout << "type: DIR" << std::endl; + std::cout << "name: " << std::string(info.name) << std::endl; + lfs_dir_t lfs_dir; + ret = fs.DirOpen(path.c_str(), &lfs_dir); + if (ret) { + std::cout << "fs.DirOpen returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + ret = fs.DirRead(&lfs_dir, &info); + while (ret > 0) { + if (ret < 0) { + std::cout << "fs.DirRead returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + if (info.type == LFS_TYPE_REG) { + std::cout << "type: REG"; + std::cout << " size: " << info.size; + std::cout << " name: " << std::string(info.name) << std::endl; + } else if (info.type == LFS_TYPE_DIR) { + std::cout << "type: DIR"; + std::cout << " name: " << std::string(info.name) << std::endl; + } else { + std::cout << "unknown type: " << info.type << std::endl; + return 1; + } + // fill for next iteration + ret = fs.DirRead(&lfs_dir, &info); + } // end of while loop + if (ret < 0) { + std::cout << "fs.DirRead returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + } else { // endif provided path + std::cout << "unknown type: " << info.type << std::endl; + return 1; + } + return 0; +} +int command_mkdir(const std::string &program_name, const std::vector &args, bool verbose) +{ + if (verbose) { + std::cout << "running command 'mkdir'" << std::endl; + } + // argv: littlefs-do mkdir path + // check for help flag first + for (const std::string &arg : args) + { + if (arg == "-h" || arg == "--help") + { + print_help_mkdir(program_name); + return 0; + } + } + if (args.empty()) { + std::cout << "error: no path given" << std::endl; + print_help_mkdir(program_name); + return 1; + } + for (const std::string &path : args) + { + if (verbose) { + std::cout << "mkdir: " << path << std::endl; + } + int ret = fs.DirCreate(path.c_str()); + if (ret < 0) { + std::cout << "fs.DirCreate returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + } + return 0; +} + +int command_rmdir(const std::string &program_name, const std::vector &args, bool verbose) +{ + if (verbose) { + std::cout << "running command 'rmdir'" << std::endl; + } + // argv: littlefs-do rmdir path + // check for help flag first + for (const std::string &arg : args) + { + if (arg == "-h" || arg == "--help") + { + print_help_rmdir(program_name); + return 0; + } + } + if (args.empty()) { + std::cout << "error: no path given" << std::endl; + print_help_rmdir(program_name); + return 1; + } + for (const std::string &path : args) + { + if (verbose) { + std::cout << "rmdir: " << path << std::endl; + } + lfs_info info; + int ret = fs.Stat(path.c_str(), &info); + if (ret) { + std::cout << "fs.Stat returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + if (info.type == LFS_TYPE_REG) { + std::cout << "error: provided path '" << path << "is a file" << std::endl; + return 1; + } + // assume non-files are directories + ret = fs.FileDelete(path.c_str()); + if (ret < 0) { + std::cout << "fs.FileDelete returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + } + return 0; +} + +int command_rm(const std::string &program_name, const std::vector &args, bool verbose) +{ + if (verbose) { + std::cout << "running command 'rm'" << std::endl; + } + // argv: littlefs-do rm path + // check for help flag first + for (const std::string &arg : args) + { + if (arg == "-h" || arg == "--help") + { + print_help_rm(program_name); + return 0; + } + } + if (args.empty()) { + std::cout << "error: no path given" << std::endl; + print_help_rm(program_name); + return 1; + } + for (const std::string &path : args) + { + if (verbose) { + std::cout << "rm: " << path << std::endl; + } + // assume non-files are directories + int ret = fs.FileDelete(path.c_str()); + if (ret < 0) { + std::cout << "fs.FileDelete returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + } + return 0; +} + +int command_cp(const std::string &program_name, const std::vector &args, bool verbose) +{ + if (verbose) { + std::cout << "running 'cp'" << std::endl; + } + for (const std::string &arg : args) + { + if (arg == "-h" || arg == "--help") + { + print_help_cp(program_name); + return 0; + } + } + if (args.size() < 2) { + std::cout << "error: no destination given, need source and destination" << std::endl; + print_help_cp(program_name); + return 1; + } + const std::string &destination = args.back(); + static constexpr size_t memorySize {0x400000}; + std::array buffer; + if (destination[0] == '/') { + if (verbose) { + std::cout << "destination starts with '/', copying files into image" << std::endl; + } + { + lfs_info info; + int ret = fs.Stat(destination.c_str(), &info); + if (ret) { + std::cout << "fs.Stat for destination path returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + if (info.type == LFS_TYPE_REG) { + std::cout << "destination is file, assumed directory" << std::endl; + return 1; + } + } + for (size_t i=0; i &args, bool verbose) +{ + if (verbose) { + std::cout << "running 'settings'" << std::endl; + } + for (const std::string &arg : args) + { + if (arg == "-h" || arg == "--help") + { + print_help_settings(program_name); + return 0; + } + } + + if (verbose) { + std::cout << "calling Settings::Init()" << std::endl; + } + settingsController.Init(); + using namespace Pinetime::Controllers; + { + auto clockface = settingsController.GetClockFace(); + auto clockface_str = [](auto val) { + if (val == 0) return "Digital"; + if (val == 1) return "Analog"; + if (val == 2) return "PineTimeStyle"; + if (val == 3) return "Terminal"; + return "unknown"; + }(clockface); + std::cout << "ClockFace: " << static_cast(clockface) << " " << clockface_str << std::endl; + } + { + auto chimes = settingsController.GetChimeOption(); + auto chimes_str = [](auto val) { + if (val == Settings::ChimesOption::None) return "None"; + if (val == Settings::ChimesOption::Hours) return "Hours"; + if (val == Settings::ChimesOption::HalfHours) return "HalfHours"; + return "unknown"; + }(chimes); + std::cout << "Chimes: " << static_cast(chimes) << " " << chimes_str << std::endl; + } + auto color_str = [](auto c) { + if (c == Settings::Colors::White) return "White"; + if (c == Settings::Colors::Silver) return "Silver"; + if (c == Settings::Colors::Gray) return "Gray"; + if (c == Settings::Colors::Black) return "Black"; + if (c == Settings::Colors::Red) return "Red"; + if (c == Settings::Colors::Maroon) return "Maroon"; + if (c == Settings::Colors::Yellow) return "Yellow"; + if (c == Settings::Colors::Olive) return "Olive"; + if (c == Settings::Colors::Lime) return "Lime"; + if (c == Settings::Colors::Green) return "Cyan"; + if (c == Settings::Colors::Teal) return "Teal"; + if (c == Settings::Colors::Blue) return "Blue"; + if (c == Settings::Colors::Navy) return "Navy"; + if (c == Settings::Colors::Magenta) return "Magenta"; + if (c == Settings::Colors::Purple) return "Purple"; + if (c == Settings::Colors::Orange) return "Orange"; + return "unknown"; + }; + std::cout << "PTSColorTime: " << color_str(settingsController.GetPTSColorTime()) << std::endl; + std::cout << "PTSColorBar: " << color_str(settingsController.GetPTSColorBar()) << std::endl; + std::cout << "PTSColorBG: " << color_str(settingsController.GetPTSColorBG()) << std::endl; + std::cout << "AppMenu: " << static_cast(settingsController.GetAppMenu()) << std::endl; + std::cout << "SettingsMenu: " << static_cast(settingsController.GetSettingsMenu()) << std::endl; + std::cout << "ClockType: " << (settingsController.GetClockType() == Settings::ClockType::H24 ? "H24" : "H12") << std::endl; + { + auto notif = settingsController.GetNotificationStatus(); + auto notif_str = [](auto val) { + if (val == Settings::Notification::ON) return "ON"; + if (val == Settings::Notification::OFF) return "OFF"; + return "unknown"; + }(notif); + std::cout << "NotificationStatus: " << static_cast(notif) << " " << notif_str << std::endl; + } + std::cout << "ScreenTimeOut: " << settingsController.GetScreenTimeOut() << " ms" << std::endl; + std::cout << "ShakeThreshold: " << settingsController.GetShakeThreshold() << std::endl; + { + std::cout << "WakeUpModes: " << std::endl; + std::cout << "- SingleTap: " << (settingsController.isWakeUpModeOn(Settings::WakeUpMode::SingleTap) ? "ON" : "OFF") << std::endl; + std::cout << "- DoubleTap: " << (settingsController.isWakeUpModeOn(Settings::WakeUpMode::DoubleTap) ? "ON" : "OFF") << std::endl; + std::cout << "- RaiseWrist: " << (settingsController.isWakeUpModeOn(Settings::WakeUpMode::RaiseWrist) ? "ON" : "OFF") << std::endl; + std::cout << "- Shake: " << (settingsController.isWakeUpModeOn(Settings::WakeUpMode::Shake) ? "ON" : "OFF") << std::endl; + } + { + auto brightness = settingsController.GetBrightness(); + auto brightness_str = [](auto val) { + if (val == BrightnessController::Levels::Off) return "Off"; + if (val == BrightnessController::Levels::Low) return "Low"; + if (val == BrightnessController::Levels::Medium) return "Medium"; + if (val == BrightnessController::Levels::High) return "High"; + return "unknown"; + }(brightness); + std::cout << "Brightness: " << static_cast(brightness) << " " << brightness_str << std::endl; + } + std::cout << "StepsGoal: " << settingsController.GetStepsGoal() << std::endl; + std::cout << "BleRadioEnabled: " << (settingsController.GetBleRadioEnabled() ? "true" : "false") << std::endl; + return 0; +} + +int main(int argc, char **argv) +{ + // parse arguments + if (argc <= 1) { + print_help_generic(argv[0]); + return 1; + } + bool verbose = false; + std::vector args; + for (int i=1; i