diff --git a/.github/workflows/test-EMULATOR.yml b/.github/workflows/test-EMULATOR.yml index e16ca8712..12d46c9c6 100644 --- a/.github/workflows/test-EMULATOR.yml +++ b/.github/workflows/test-EMULATOR.yml @@ -2,9 +2,9 @@ name: Build OSW Emulator (Ubuntu) on: push: - paths_ignore: '["**/*.md", "**/scripts/screen_capture/**"]' + paths-ignore: ["**/*.md", "**/scripts/screen_capture/**"] pull_request: - branches: [ master, develop ] + branches: [master, develop] jobs: check_skip: @@ -15,8 +15,8 @@ jobs: - id: skip_check uses: fkirc/skip-duplicate-actions@v5.2.0 with: - concurrent_skipping: 'same_content_newer' - skip_after_successful_duplicate: 'true' + concurrent_skipping: "same_content_newer" + skip_after_successful_duplicate: "true" build-EMULATOR: runs-on: ubuntu-22.04 @@ -25,24 +25,36 @@ jobs: strategy: matrix: build-configuration: [Debug, Release] - steps: - - name: Checkout repository and submodules - # uses: actions/checkout@v3 # incompatible with git lfs cache bandwidth calcs (https://github.com/actions/checkout/issues/165) - uses: nschloe/action-cached-lfs-checkout@v1.2.1 - with: - submodules: recursive - lfs: 'true' - - name: Update packages - run: sudo apt-get update - - name: Install packages - run: sudo apt-get -y install gcc g++ cmake libsdl2-dev libsdl2-image-dev python3 python3-pip - - name: Install python packages - run: pip3 install --user -r scripts/requirements.txt - - name: Create build directory - run: mkdir build - - name: CMake ${{ matrix.build-configuration }} - run: cd build && cmake -DCMAKE_BUILD_TYPE=${{ matrix.build-configuration }} .. - - name: Make - run: cd build && make -j $(nproc) - - name: Test - run: cd build && make test + steps: + # the following is incompatible with git lfs cache bandwidth calculations (https://github.com/actions/checkout/issues/165) + # -> we removed the following and replaced it with a cache-aware checkout + # - name: Checkout repository and submodules + # uses: actions/checkout@v3 + # with: + # submodules: recursive + # lfs: 'true' + - name: Checkout repository and submodules + uses: nschloe/action-cached-lfs-checkout@v1.2.1 + with: + submodules: recursive + - name: Update packages + run: sudo apt-get update + - name: Install packages + run: sudo apt-get -y install gcc g++ cmake libsdl2-dev libsdl2-image-dev python3 python3-pip + - name: Install python packages + run: pip3 install --user -r scripts/requirements.txt + - name: Create build directory + run: mkdir build + - name: CMake ${{ matrix.build-configuration }} + run: cd build && cmake -DCMAKE_BUILD_TYPE=${{ matrix.build-configuration }} .. + - name: Make + run: cd build && make -j $(nproc) + - name: Test + run: cd build && make test + - name: Upload test artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: test-results + path: | + build/Testing diff --git a/.github/workflows/test-FEATURE.yml b/.github/workflows/test-FEATURE.yml index d6d96c740..73cc12d6c 100644 --- a/.github/workflows/test-FEATURE.yml +++ b/.github/workflows/test-FEATURE.yml @@ -2,9 +2,9 @@ name: Build OSW-OS (Ubuntu, additional features) on: push: - paths_ignore: '["*.md", "**/scripts/screen_capture/**"]' + paths-ignore: ["*.md", "**/scripts/screen_capture/**"] pull_request: - branches: [ master, develop ] + branches: [master, develop] jobs: check_skip: @@ -15,20 +15,18 @@ jobs: - id: skip_check uses: fkirc/skip-duplicate-actions@v5.2.0 with: - concurrent_skipping: 'same_content_newer' - skip_after_successful_duplicate: 'true' + concurrent_skipping: "same_content_newer" + skip_after_successful_duplicate: "true" Find-feature: runs-on: ubuntu-latest needs: check_skip if: ${{ needs.check_skip.outputs.should_skip != 'true' }} - steps: + steps: - name: Checkout repository and submodules - # uses: actions/checkout@v3 # incompatible with git lfs cache bandwidth calcs (https://github.com/actions/checkout/issues/165) uses: nschloe/action-cached-lfs-checkout@v1.2.1 with: submodules: recursive - lfs: 'true' - id: get-flag run: | echo "feature=$(python3 .github/getWorkflowMatrix.py all_flags)" >> $GITHUB_OUTPUT @@ -52,31 +50,29 @@ jobs: model: ${{ fromJson(needs.Find-feature.outputs.default_model) }} language: ${{ fromJson(needs.Find-feature.outputs.default_language) }} steps: - - name: Checkout repository and submodules - # uses: actions/checkout@v3 # incompatible with git lfs cache bandwidth calcs (https://github.com/actions/checkout/issues/165) - uses: nschloe/action-cached-lfs-checkout@v1.2.1 - with: - submodules: recursive - lfs: 'true' - - name: Cache pip - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: pip-${{ runner.os }} - - name: Cache PlatformIO - uses: actions/cache@v3 - with: - path: ~/.platformio - key: platformio-${{ runner.os }} - - name: Install swig - run: sudo apt-get update && sudo apt-get -y install swig - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - name: Install PlatformIO - run: python -m pip install --upgrade pip && pip install --upgrade platformio - - name: Rename config - run: mv include/config.h.example include/config.h - - name: Compile language ${{ matrix.language }} model ${{ matrix.model }} feature ${{ matrix.feature }} - run: python3 .github/buildFirmware.py -l "${{ matrix.language }}" -m "${{ matrix.model }}" -f "${{ matrix.feature }}" -b debug + - name: Checkout repository and submodules + uses: nschloe/action-cached-lfs-checkout@v1.2.1 + with: + submodules: recursive + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: pip-${{ runner.os }} + - name: Cache PlatformIO + uses: actions/cache@v3 + with: + path: ~/.platformio + key: platformio-${{ runner.os }} + - name: Install swig + run: sudo apt-get update && sudo apt-get -y install swig + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - name: Install PlatformIO + run: python -m pip install --upgrade pip && pip install --upgrade platformio + - name: Rename config + run: mv include/config.h.example include/config.h + - name: Compile language ${{ matrix.language }} model ${{ matrix.model }} feature ${{ matrix.feature }} + run: python3 .github/buildFirmware.py -l "${{ matrix.language }}" -m "${{ matrix.model }}" -f "${{ matrix.feature }}" -b debug diff --git a/.github/workflows/test-OS.yml b/.github/workflows/test-OS.yml index 20b2fbf26..1c26dff0c 100644 --- a/.github/workflows/test-OS.yml +++ b/.github/workflows/test-OS.yml @@ -4,18 +4,16 @@ on: workflow_dispatch: pull_request: schedule: - - cron: '0 0 * * *' + - cron: "0 0 * * *" jobs: Find-feature: runs-on: ubuntu-latest - steps: + steps: - name: Checkout repository and submodules - # uses: actions/checkout@v3 # incompatible with git lfs cache bandwidth calcs (https://github.com/actions/checkout/issues/165) uses: nschloe/action-cached-lfs-checkout@v1.2.1 with: submodules: recursive - lfs: 'true' - uses: dorny/paths-filter@v2.11.1 id: filter with: @@ -45,34 +43,32 @@ jobs: model: ${{ fromJson(needs.Find-feature.outputs.get-models) }} language: ${{ fromJson(needs.Find-feature.outputs.get-languages) }} steps: - - name: Checkout repository and submodules - # uses: actions/checkout@v3 # incompatible with git lfs cache bandwidth calcs (https://github.com/actions/checkout/issues/165) - uses: nschloe/action-cached-lfs-checkout@v1.2.1 - with: - submodules: recursive - lfs: 'true' - - name: Cache pip - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: pip-${{ runner.os }} - - name: Cache PlatformIO - uses: actions/cache@v3 - with: - path: ~/.platformio - key: platformio-${{ runner.os }} - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' + - name: Checkout repository and submodules + uses: nschloe/action-cached-lfs-checkout@v1.2.1 + with: + submodules: recursive + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: pip-${{ runner.os }} + - name: Cache PlatformIO + uses: actions/cache@v3 + with: + path: ~/.platformio + key: platformio-${{ runner.os }} + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" - - name: Install PlatformIO - shell: bash - run: | - apt update && apt -y install swig - python -m pip install --upgrade pip - pip install --upgrade platformio - mv include/config.h.example include/config.h + - name: Install PlatformIO + shell: bash + run: | + apt update && apt -y install swig + python -m pip install --upgrade pip + pip install --upgrade platformio + mv include/config.h.example include/config.h - - name: Compile language ${{ matrix.language }} model ${{ matrix.model }} - run: python3 .github/buildFirmware.py -l "${{ matrix.language }}" -m "${{ matrix.model }}" -b debug + - name: Compile language ${{ matrix.language }} model ${{ matrix.model }} + run: python3 .github/buildFirmware.py -l "${{ matrix.language }}" -m "${{ matrix.model }}" -b debug diff --git a/.github/workflows/test-OSW.yml b/.github/workflows/test-OSW.yml index 3ba7758fd..3fbff9b88 100644 --- a/.github/workflows/test-OSW.yml +++ b/.github/workflows/test-OSW.yml @@ -3,9 +3,9 @@ name: Build OSW-OS (Ubuntu, all models) on: workflow_dispatch: push: - paths_ignore: '["*.md", "**/scripts/screen_capture/**"]' + paths-ignore: ["*.md", "**/scripts/screen_capture/**"] pull_request: - branches: [ master, develop ] + branches: [master, develop] jobs: check_skip: @@ -16,20 +16,18 @@ jobs: - id: skip_check uses: fkirc/skip-duplicate-actions@v5.2.0 with: - concurrent_skipping: 'same_content_newer' - skip_after_successful_duplicate: 'true' + concurrent_skipping: "same_content_newer" + skip_after_successful_duplicate: "true" Find-packages: runs-on: ubuntu-latest needs: check_skip if: ${{ needs.check_skip.outputs.should_skip != 'true' }} - steps: + steps: - name: Checkout repository and submodules - # uses: actions/checkout@v3 # incompatible with git lfs cache bandwidth calcs (https://github.com/actions/checkout/issues/165) uses: nschloe/action-cached-lfs-checkout@v1.2.1 with: submodules: recursive - lfs: 'true' - uses: dorny/paths-filter@v2.11.1 id: filter with: @@ -58,37 +56,35 @@ jobs: model: ${{ fromJson(needs.Find-packages.outputs.models_matrix) }} build-configuration: [debug, release] steps: - - name: Checkout repository and submodules - # uses: actions/checkout@v3 # incompatible with git lfs cache bandwidth calcs (https://github.com/actions/checkout/issues/165) - uses: nschloe/action-cached-lfs-checkout@v1.2.1 - with: - submodules: recursive - lfs: 'true' - - name: Cache pip - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: pip-${{ runner.os }} - - name: Cache PlatformIO - uses: actions/cache@v3 - with: - path: ~/.platformio - key: platformio-${{ runner.os }} - - name: Install swig - run: sudo apt-get update && sudo apt-get -y install swig - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - name: Install PlatformIO - run: python -m pip install --upgrade pip && pip install --upgrade platformio - - name: Rename config - run: mv include/config.h.example include/config.h - - name: Compile language ${{ matrix.language }} model ${{ matrix.model }} - run: python3 .github/buildFirmware.py -l "${{ matrix.language }}" -m "${{ matrix.model }}" -b "${{ matrix.build-configuration }}" - - name: Upload firmware artifacts - uses: actions/upload-artifact@v3 - with: - name: firmwares - path: | - *.bin + - name: Checkout repository and submodules + uses: nschloe/action-cached-lfs-checkout@v1.2.1 + with: + submodules: recursive + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: pip-${{ runner.os }} + - name: Cache PlatformIO + uses: actions/cache@v3 + with: + path: ~/.platformio + key: platformio-${{ runner.os }} + - name: Install swig + run: sudo apt-get update && sudo apt-get -y install swig + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - name: Install PlatformIO + run: python -m pip install --upgrade pip && pip install --upgrade platformio + - name: Rename config + run: mv include/config.h.example include/config.h + - name: Compile language ${{ matrix.language }} model ${{ matrix.model }} + run: python3 .github/buildFirmware.py -l "${{ matrix.language }}" -m "${{ matrix.model }}" -b "${{ matrix.build-configuration }}" + - name: Upload firmware artifacts + uses: actions/upload-artifact@v3 + with: + name: firmwares + path: | + *.bin diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bd1d0d85..446455932 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,7 +142,8 @@ target_compile_definitions(emulator.run PUBLIC # Comment these as you wish... OSW_FEATURE_STATS_STEPS OSW_FEATURE_WEATHER - OSW_APPS_EXAMPLES=1 + OSW_SERVICE_CONSOLE + OSW_APPS_EXAMPLES GAME_SNAKE=1 GAME_BRICK_BREAKER=1 TOOL_FLASHLIGHT=1 diff --git a/README.md b/README.md index 3c6e62857..cd93cfa18 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ If you want to compile for a specific model, you can use the `-e` flag with an ` ## Hack it! To get started, take a look into the examples in the `src/apps/examples` folder - or just into any other app. If you want to compile the examples or other (by default) excluded applications, take a look into the `main.cpp` file and add the respective flags to the `platformio.ini` file. -## Debugging (CLI) +## CLI If you want to print out the log for debugging (also including decoded exception traces), use the following command: @@ -59,6 +59,19 @@ If you want to print out the log for debugging (also including decoded exception $ pio device monitor ``` +In this serial console you also have the ability (beside much more) to configure the watch - just type in `help` to get started: +``` +OSW > help +Available commands: + configure - enter configuration mode + help - show this help + hostname - show the device hostname + lock - lock the console + reboot - warm-start the device forcefully + time - show current UTC time + wipe - format NVS partition and reset configuration +``` + ## Creating Screenshots of your Apps

@@ -98,6 +111,7 @@ $ cd scripts/screen_capture/ $ ./createScreenshot.sh ``` * The captured file can be found in the `screenshot/` folder inside the `open-smartwatch-os` directory. + ## Troubleshooting For more information on troubleshooting, see [Wiki](https://open-smartwatch.github.io/resources/firmware/#troubleshooting). diff --git a/docs/firmware/osw_os.md b/docs/firmware/osw_os.md index bef39b9bd..12bb66ab1 100644 --- a/docs/firmware/osw_os.md +++ b/docs/firmware/osw_os.md @@ -12,6 +12,15 @@ Flag | Description | Requirements `GPS_EDITION` | Configure the build for use with GPS (including apps, api, sensors) | `PROGMEM_TILES`, `BOARD_HAS_PSRAM` `GPS_EDITION_ROTATED` | Replacement for `GPS_EDITION` to work with flipped boards | - +## Example Flags + +You want to know how to use some example code or see it in action? These flags enable vairous features that are not enabled by default - just search inside the source code for the flag to see how it works and what it does. + +Flag | Description | Requirements +----------- | ----------- | ----------- +`OSW_SERVICE_EXAMPLE` | Enable the example code to demonstrate how to write on services. | - +`OSW_APPS_EXAMPLES` | Enable the example code to demonstrate how to write own apps (v1/v2). | - + ## Experimental Flags These flags should be available on all models. Because they are experimental, they are not enabled by default any may won't work or even compile. diff --git a/emulator/include/Serial.h b/emulator/include/Serial.h index 694290f88..d4c610140 100644 --- a/emulator/include/Serial.h +++ b/emulator/include/Serial.h @@ -9,7 +9,7 @@ class Serial_t { public: - Serial_t() {}; + Serial_t(); ~Serial_t() {}; std::list buffer; @@ -40,8 +40,12 @@ class Serial_t { this->println(); } + int available(); + int read(); + void println(); private: + std::list inputBuffer; int bauds = 0; bool buffered = false; bool addBufferNewline = true; diff --git a/emulator/src/Serial.cpp b/emulator/src/Serial.cpp index 25cc0b678..fae25e22d 100644 --- a/emulator/src/Serial.cpp +++ b/emulator/src/Serial.cpp @@ -1,8 +1,18 @@ #include "../include/Serial.h" #include +#include +#include +#include + Serial_t Serial; +Serial_t::Serial_t() { + // put the stdin-stream into non-blocking mode to avoid hanging in case available() is called + int flags = fcntl(STDIN_FILENO, F_GETFL, 0); + fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); +} + void Serial_t::setBuffered(bool buffered) { this->buffered = buffered; this->addBufferNewline = true; @@ -26,4 +36,21 @@ void Serial_t::println() { this->addBufferNewline = true; else std::cout << std::endl; +} + +int Serial_t::available() { + char c; + if(::read(STDIN_FILENO, &c, 1) > 0) { + this->inputBuffer.push_back(c); + return true; + } + return false; +} + +int Serial_t::read() { + if(this->inputBuffer.empty()) + return -1; + char c = this->inputBuffer.front(); + this->inputBuffer.pop_front(); + return c; } \ No newline at end of file diff --git a/emulator/src/tests/unitTests/logging.cpp b/emulator/src/tests/unitTests/logging.cpp index b75378090..601d6abd9 100644 --- a/emulator/src/tests/unitTests/logging.cpp +++ b/emulator/src/tests/unitTests/logging.cpp @@ -10,7 +10,7 @@ * be bound to the stack-frame of its test - and destroyed upon leaving. */ -#define EXPECT_LASTLINE(expect) EXPECT_STREQ(expect, Serial.buffer.back().str().c_str()) +#define EXPECT_LASTLINE(expect) EXPECT_STREQ(expect, (Serial.buffer.size() ? Serial.buffer.back().str().c_str() : "")) // Defines to use check log messages (all except debug!) #ifndef NDEBUG diff --git a/include/osw_config.h b/include/osw_config.h index 43e97239e..603faaf18 100644 --- a/include/osw_config.h +++ b/include/osw_config.h @@ -47,6 +47,7 @@ class OswConfig { String getCategoriesJson(); String getFieldJson(String id); void setField(String id, String value); + void resetField(String id); void notifyChange(); protected: Preferences prefs; // for the config keys accessible diff --git a/include/services/OswServiceTaskConsole.h b/include/services/OswServiceTaskConsole.h new file mode 100644 index 000000000..dfb23eb19 --- /dev/null +++ b/include/services/OswServiceTaskConsole.h @@ -0,0 +1,22 @@ +#pragma once +#include "osw_service.h" + +class OswServiceTaskConsole : public OswServiceTask { + public: + OswServiceTaskConsole() {}; + virtual void setup() override; + virtual void loop() override; + virtual void stop() override; + ~OswServiceTaskConsole() {}; + + private: + void newPrompt(); + void showPrompt(); + void runPrompt(); + void showHelp(); + + std::string m_inputBuffer; + bool m_locked = false; + unsigned char m_lockCounter = 0; + bool m_configuring = false; +}; \ No newline at end of file diff --git a/include/services/OswServiceTaskExample.h b/include/services/OswServiceTaskExample.h index 687f744c0..763d6a9f4 100644 --- a/include/services/OswServiceTaskExample.h +++ b/include/services/OswServiceTaskExample.h @@ -1,6 +1,5 @@ -#ifndef OSW_SERVICE_TASKEXAMPLE_H -#define OSW_SERVICE_TASKEXAMPLE_H - +#pragma once +#include #include "osw_service.h" class OswServiceTaskExample : public OswServiceTask { @@ -14,5 +13,3 @@ class OswServiceTaskExample : public OswServiceTask { private: time_t printLimit = 0; }; - -#endif \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 7ff9a6281..02960c9fe 100755 --- a/platformio.ini +++ b/platformio.ini @@ -44,6 +44,7 @@ build_unflags = -std=gnu++11 # The correct flag will be set by the cppflags pyth build_flags = -D OSW_TARGET_PLATFORM_HEADER='"platform/LIGHT_EDITION_V3_3.h"' -D OSW_FEATURE_STATS_STEPS + -D OSW_SERVICE_CONSOLE -D OSW_FEATURE_WIFI build_type = debug @@ -51,6 +52,7 @@ build_type = debug build_flags = -D OSW_TARGET_PLATFORM_HEADER='"platform/LIGHT_EDITION_V4_0.h"' -D OSW_FEATURE_STATS_STEPS + -D OSW_SERVICE_CONSOLE -D OSW_FEATURE_WIFI -D OSW_FEATURE_WIFI_ONBOOT build_type = debug @@ -60,6 +62,7 @@ build_type = debug build_flags = -D OSW_TARGET_PLATFORM_HEADER='"platform/LIGHT_EDITION_V3_3.h"' -D OSW_FEATURE_LUA + -D OSW_SERVICE_CONSOLE -D OSW_FEATURE_WIFI -D LUA_C89_NUMBERS ; Required by OSW_FEATURE_LUA extra_scripts = @@ -78,6 +81,7 @@ build_flags = -D BOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -D OSW_FEATURE_STATS_STEPS + -D OSW_SERVICE_CONSOLE -D OSW_FEATURE_WIFI -D OSW_FEATURE_WIFI_APST -D OSW_FEATURE_WIFI_ONBOOT @@ -92,6 +96,7 @@ build_flags = -D BOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -D OSW_FEATURE_STATS_STEPS + -D OSW_SERVICE_CONSOLE -D OSW_FEATURE_WIFI -D OSW_FEATURE_WIFI_APST -D OSW_FEATURE_WIFI_ONBOOT diff --git a/src/main.cpp b/src/main.cpp index 661c111e1..553fd4ab7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -205,7 +205,7 @@ void loop() { main_mainDrawer.registerApp("GPS", new OswAppV2Compat("osw.gps.mc", "Magnetometer Calibrate", gpsOswAppMC)); #endif -#if OSW_APPS_EXAMPLES == 1 +#ifdef OSW_APPS_EXAMPLES // For a short "How to write your own apps" see the examples below main_mainDrawer.registerAppLazy(LANG_EXAMPLES); // only v2 apps support lazy loading (for now) static OswAppExampleV1 exampleV1; // this is a static object, so it will be kept in memory until the device is reset - but it kind of defeats the purpose of lazy loading (static object = initialized on bootup, not on first use) diff --git a/src/osw_config.cpp b/src/osw_config.cpp index 4219ec2be..8125ed946 100644 --- a/src/osw_config.cpp +++ b/src/osw_config.cpp @@ -87,6 +87,7 @@ void OswConfig::reset(bool clearWholeNVS) { bool res; if(clearWholeNVS) { this->prefs.end(); + OSW_LOG_D("Formatting NVS partition..."); res = nvs_flash_erase() == ESP_OK; assert(res); res = nvs_flash_init() == ESP_OK; @@ -155,6 +156,18 @@ void OswConfig::setField(String id, String value) { this->notifyChange(); } +void OswConfig::resetField(String id) { + unsigned char i = 0; + for (; i < oswConfigKeysCount; i++) { + OswConfigKey* key = oswConfigKeys[i]; + if(String(key->id) == id) { + key->fromString(key->toDefaultString().c_str()); + break; + } + } + this->notifyChange(); +} + void OswConfig::notifyChange() { // Reload parts of the OS, which buffer values // OswUI::getInstance()->resetTextColors(); // nope - this is done by the ui itself diff --git a/src/services/OswServiceTaskConsole.cpp b/src/services/OswServiceTaskConsole.cpp new file mode 100644 index 000000000..b77ced93d --- /dev/null +++ b/src/services/OswServiceTaskConsole.cpp @@ -0,0 +1,187 @@ +#include "./services/OswServiceTaskConsole.h" +#include "osw_hal.h" + +void OswServiceTaskConsole::setup() { + OswServiceTask::setup(); + OSW_LOG_I("Console is now enabled."); +#ifdef NDEBUG + this-> m_locked = true; // in release mode the console is locked by default +#endif + this->newPrompt(); +} + +void OswServiceTaskConsole::loop() { + while(true) { + if(!Serial.available()) break; + char c = Serial.read(); + switch (c) { + case 10: // LF + case 13: // CR + this->runPrompt(); + this->newPrompt(); + break; + case 8: // backspace + case 127: // delete + case 27: // escape + if(this->m_inputBuffer.length() > 0) { + this->m_inputBuffer.pop_back(); + } + Serial.print(c); + break; + case 9: // tab + Serial.println(); // finish the prompt line + this->showHelp(); + this->showPrompt(); + break; + default: + if(32 <= c and c <= 126) { // printable characters + this->m_inputBuffer += c; + Serial.print(c); // echo the entered character back + } else { + Serial.print((char) 0x07); // bell + OSW_LOG_D("Unprocessable character (",(int) c, "): ", c); + } + } + } +} + +void OswServiceTaskConsole::newPrompt() { + this->m_inputBuffer.clear(); + if(this->m_locked) return; + this->showPrompt(); +} + +void OswServiceTaskConsole::showPrompt() { + if (this->m_configuring) { + Serial.print("OSW (configure) > "); + } else { + Serial.print("OSW > "); + } + if(!this->m_inputBuffer.empty()) { + Serial.print(this->m_inputBuffer.c_str()); + } +} + +void OswServiceTaskConsole::runPrompt() { + Serial.println(); // finish the prompt line + if(this->m_inputBuffer.empty()) return; + if(this->m_locked) { + Serial.println("Console is locked! Enter \"unlock\" to unlock."); + if(this->m_inputBuffer == "unlock") { + this->m_locked = false; + } + return; + } + // command convention: show only what was asked (preferably machine readable), only on error be verbose + bool processed = true; + if (this->m_configuring) { + if (this->m_inputBuffer == "clear") { + OswConfig::getInstance()->reset(false); + } else if (this->m_inputBuffer == "exit") { + this->m_configuring = false; + } else if (this->m_inputBuffer.find("get ") == 0 and this->m_inputBuffer.length() > 4) { + auto key = this->m_inputBuffer.substr(4); + for (auto i = 0; i < oswConfigKeysCount; i++) { + if(oswConfigKeys[i]->id == key) { + Serial.println(oswConfigKeys[i]->toString()); + break; + } + } + } else if (this->m_inputBuffer.find("info ") == 0 and this->m_inputBuffer.length() > 5) { + auto key = this->m_inputBuffer.substr(5); + Serial.println(OswConfig::getInstance()->getFieldJson(key.c_str())); + } else if (this->m_inputBuffer == "help") { + this->showHelp(); + } else if (this->m_inputBuffer == "list") { + for (auto i = 0; i < oswConfigKeysCount; i++) { + Serial.println(oswConfigKeys[i]->id); + } + } else if (this->m_inputBuffer.find("reset ") == 0 and this->m_inputBuffer.length() > 7) { + auto key = this->m_inputBuffer.substr(7); + OswConfig::getInstance()->enableWrite(); + OswConfig::getInstance()->resetField(key.c_str()); + OswConfig::getInstance()->disableWrite(); + } else if (this->m_inputBuffer.find("set ") == 0 and this->m_inputBuffer.length() > 5) { + auto key = this->m_inputBuffer.substr(4); // " " + auto space = key.find(" ", 0); + if (space == std::string::npos) { + Serial.println("Invalid command."); + return; + } + auto value = key.substr(space + 1); // "" + key = key.substr(0, space); // "" + OswConfig::getInstance()->enableWrite(); + OswConfig::getInstance()->setField(key.c_str(), value.c_str()); + OswConfig::getInstance()->disableWrite(); + } else { + processed = false; + } + } else { + if (this->m_inputBuffer == "configure") { + this->m_configuring = true; + } else if (this->m_inputBuffer == "help") { + this->showHelp(); +#ifdef OSW_FEATURE_WIFI + } else if (this->m_inputBuffer == "hostname") { + Serial.println(OswConfigAllKeys::hostname.get()); +#endif + } else if (this->m_inputBuffer == "lock") { + this->m_locked = true; +#ifndef OSW_EMULATOR + } else if (this->m_inputBuffer == "reboot") { + // this does not work in the emulator as it is running under an own thread, of which the shutdown-exception is not captured - populating here and crashing + ESP.restart(); +#endif + } else if (this->m_inputBuffer == "time") { + Serial.println(OswHal::getInstance()->getUTCTime()); + } else if (this->m_inputBuffer == "wipe") { + OswConfig::getInstance()->reset(true); + } else { + processed = false; + } + } + // show help if the command was not processed + if (!processed) { + Serial.println("Unknown command."); + Serial.println(); + this->showHelp(); + this->m_lockCounter++; + } + // in case of garbage input, lock the console after 16 attempts + if (this->m_lockCounter > 16) { + this->m_locked = true; + this->m_lockCounter = 0; + } +} + +void OswServiceTaskConsole::showHelp() { + Serial.println("Available commands:"); + // let's try to be civil and show the commands in alphabetical order + if (this->m_configuring) { + Serial.println(" clear - reset all keys to default values"); + Serial.println(" exit - leave configuration mode"); + Serial.println(" get - get a value for a key"); + Serial.println(" help - show this help"); + Serial.println(" info - show more information about a key"); + Serial.println(" list - show all keys"); + Serial.println(" reset - reset a key to default value"); + Serial.println(" set - set a value for a key (value is everything until the end of the line)"); + } else { + Serial.println(" configure - enter configuration mode"); + Serial.println(" help - show this help"); +#ifdef OSW_FEATURE_WIFI + Serial.println(" hostname - show the device hostname"); +#endif + Serial.println(" lock - lock the console"); +#ifndef OSW_EMULATOR + Serial.println(" reboot - warm-start the device forcefully"); +#endif + Serial.println(" time - show current UTC time"); + Serial.println(" wipe - format NVS partition and reset configuration"); + } +} + +void OswServiceTaskConsole::stop() { + OSW_LOG_I("Console is now disabled."); + OswServiceTask::stop(); +} \ No newline at end of file diff --git a/src/services/OswServiceTaskExample.cpp b/src/services/OswServiceTaskExample.cpp index fa0084361..252f7f7e4 100644 --- a/src/services/OswServiceTaskExample.cpp +++ b/src/services/OswServiceTaskExample.cpp @@ -1,6 +1,5 @@ -#include "./services/OswServiceTaskExample.h" #include "osw_hal.h" -#include +#include "./services/OswServiceTaskExample.h" void OswServiceTaskExample::setup() { OswServiceTask::setup(); @@ -16,6 +15,6 @@ void OswServiceTaskExample::loop() { } void OswServiceTaskExample::stop() { - OswServiceTask::stop(); OSW_LOG_I(__FUNCTION__, "()"); -} \ No newline at end of file + OswServiceTask::stop(); +} diff --git a/src/services/OswServiceTaskWebserver.cpp b/src/services/OswServiceTaskWebserver.cpp index 9a03ba758..4311d5e59 100644 --- a/src/services/OswServiceTaskWebserver.cpp +++ b/src/services/OswServiceTaskWebserver.cpp @@ -297,8 +297,8 @@ void OswServiceTaskWebserver::loop() { } void OswServiceTaskWebserver::stop() { - OswServiceTask::stop(); this->disableWebserver(); //Make sure the webserver is also stopped + OswServiceTask::stop(); } void OswServiceTaskWebserver::enableWebserver() { diff --git a/src/services/OswServiceTaskWiFi.cpp b/src/services/OswServiceTaskWiFi.cpp index 867092872..3c15dd6e1 100644 --- a/src/services/OswServiceTaskWiFi.cpp +++ b/src/services/OswServiceTaskWiFi.cpp @@ -137,8 +137,8 @@ void OswServiceTaskWiFi::selectCredentials() { } void OswServiceTaskWiFi::stop() { - OswServiceTask::stop(); this->disableWiFi(); + OswServiceTask::stop(); } /** diff --git a/src/services/OswServiceTasks.cpp b/src/services/OswServiceTasks.cpp index 45aa19ff6..8913eeec1 100644 --- a/src/services/OswServiceTasks.cpp +++ b/src/services/OswServiceTasks.cpp @@ -5,6 +5,7 @@ #include "services/OswServiceTaskGPS.h" #include "services/OswServiceTaskMemMonitor.h" #include "services/OswServiceTaskNotifier.h" +#include "services/OswServiceTaskConsole.h" #ifdef OSW_FEATURE_WIFI #include "services/OswServiceTaskWiFi.h" #include "services/OswServiceTaskWebserver.h" @@ -12,6 +13,9 @@ #include "osw_util.h" namespace OswServiceAllTasks { +#ifdef OSW_SERVICE_EXAMPLE +OswServiceTaskExample example; +#endif // OswServiceTaskExample example; #if SERVICE_BLE_COMPANION == 1 OswServiceTaskBLECompanion bleCompanion; @@ -30,6 +34,9 @@ OswServiceTaskNotifier notifier; #ifndef OSW_EMULATOR OswServiceTaskMemMonitor memory; #endif +#ifdef OSW_SERVICE_CONSOLE +OswServiceTaskConsole console; +#endif } // namespace OswServiceAllTasks OswServiceTask* oswServiceTasks[] = { @@ -40,13 +47,18 @@ OswServiceTask* oswServiceTasks[] = { & OswServiceAllTasks::gps, #endif -//&OswServiceAllTasks::example, +#ifdef OSW_SERVICE_EXAMPLE + & OswServiceAllTasks::example, +#endif #ifdef OSW_FEATURE_WIFI & OswServiceAllTasks::wifi, &OswServiceAllTasks::webserver, #endif #if OSW_SERVICE_NOTIFIER == 1 & OswServiceAllTasks::notifier, #endif +#ifdef OSW_SERVICE_CONSOLE + & OswServiceAllTasks::console, +#endif #ifndef OSW_EMULATOR #ifndef NDEBUG & OswServiceAllTasks::memory