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 8ebb0b3b3..446455932 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -141,7 +141,9 @@ target_compile_definitions(emulator.run PUBLIC
# LOCALE="locales/en-US.h"
# Comment these as you wish...
OSW_FEATURE_STATS_STEPS
- OSW_APPS_EXAMPLES=1
+ OSW_FEATURE_WEATHER
+ 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/apps/OswWeather.md b/docs/firmware/apps/OswWeather.md
index 874d358c4..319cb832f 100644
--- a/docs/firmware/apps/OswWeather.md
+++ b/docs/firmware/apps/OswWeather.md
@@ -1,5 +1,7 @@
-# OSW WEATHER
+# OSW WEATHER
+![](/assets/apps/OswWeather/h.png)
+Author: [@LorenzoSciacca](https://github.com/Lorenzosciacca)
## How to install
Add the flag `OSW_FEATURE_WEATHER` to the file `platformio.ini`:
```ini
@@ -58,7 +60,8 @@ The / \ and \ / arrows are used to decrease/increase the current selection (da
- Humidity: [relative humidity](https://en.wikipedia.org/wiki/Humidity#Relative_humidity)
- Pressure: [hPa](https://en.wikipedia.org/wiki/Pascal_(unit)#Multiples_and_submultiples)
-### Weather conditions
+### Weather conditions
+
Visit [this link](https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2) for a more detailed description of each weather condition.
@@ -164,4 +167,10 @@ Visit [this link](https://openweathermap.org/weather-conditions#Weather-Conditio
Unknown |
|
-
\ No newline at end of file
+
+
+## Using the emulator
+To use this app while using the OSW emulator, in order to retireve the data, it is necessary to perform the API request using the browser and to save the response in `file_weather.json` in the `/build` folder.
+
+
+
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/Arduino_GFX.h b/emulator/include/Arduino_GFX.h
index f8516b66a..a53ff0173 100644
--- a/emulator/include/Arduino_GFX.h
+++ b/emulator/include/Arduino_GFX.h
@@ -3,6 +3,7 @@
class Graphics2DPrint;
typedef Graphics2DPrint Arduino_GFX;
-#define GFX_NOT_DEFINED (-1)
+// in accordance to Arduino_DataBus.h also provide this define
+#define GFX_NOT_DEFINED -1
#include "Arduino_G.h"
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/apps/_experiments/OswAppWeather.h b/include/apps/_experiments/OswAppWeather.h
index 995884680..7e89ecfd1 100644
--- a/include/apps/_experiments/OswAppWeather.h
+++ b/include/apps/_experiments/OswAppWeather.h
@@ -3,6 +3,7 @@
#include
#include
#include
+#include "ArduinoJson.h"
#include "OswAppWeatherIconPrinter.h"
class OswAppWeather : public OswApp {
@@ -20,6 +21,52 @@ class OswAppWeather : public OswApp {
virtual void stop() override;
~OswAppWeather() {};
private:
+ class WeatherDecoder {
+ public:
+ WeatherDecoder(const String& input_String);
+ bool strIsValid();
+ time_t getTime();
+ std::vector getUpdates();
+ private:
+ time_t _str2time(const String& t);
+ int _str2temp(const String& temp);
+ int _str2hum(const String& humidity);
+ int _str2pres(const String& pressure);
+ int _str2wthr(const String& weather);
+ bool in_ok = true;
+ int nUpdates = 0;
+ String in_String;
+ };
+
+ class WeatherParser {
+ public:
+ WeatherParser();
+ std::optional encodeWeather(DynamicJsonDocument& doc);
+ private:
+ int _getWCond(int weather_code);
+ int cnt;
+ std::vector updates;
+ std::vectorclearCode{800};//0
+ std::vectorcloudsMin{801};//1
+ std::vectorcloudsMed{802};//2
+ std::vectorcloudsHigh{803, 804};//3
+ std::vectormist{701};//4
+ std::vectorfog{741};//5
+ std::vectorsnowMin{611, 612, 615, 616};//6
+ std::vectorsnowMed{600, 613, 601, 620};//7
+ std::vectorsnowHigh{602, 621, 622};//8
+ std::vectorrainMin{500, 300, 301, 302, 310, 311, 312, 313, 314, 321};//9
+ std::vectorrainMed{502, 501};//10
+ std::vectorrainHigh{503, 504, 511, 520, 521, 522, 531};//11
+ std::vectorthunderstorm{200, 201, 210, 211, 231, 230};//12
+ std::vectorthunderstormHeavy{202, 212, 221, 232};//13
+ std::vectorsquallTornado{771, 781};//14
+ //15 ->unknown
+ std::vector>weather_conditions{clearCode, cloudsMin, cloudsMed, cloudsHigh, mist, fog, snowMin, snowMed,
+ snowHigh, rainMin, rainMed, rainHigh, thunderstorm,
+ thunderstormHeavy, squallTornado };
+ };
+
void getW();
void printW();
void drawLayout();
@@ -46,7 +93,7 @@ class OswAppWeather : public OswApp {
uint8_t daySelector = 0;
uint8_t placeSelector = 0;
uint8_t updtSelector = 0;
- OswHal* hal = OswHal::getInstance();
+ OswHal* hal = nullptr;
std::vector forecast;//one update each 3h
time_t initTimestamp;
char initTimeMD[6];
@@ -60,5 +107,6 @@ class OswAppWeather : public OswApp {
tm* tmInit;
std::vector dayFirstUpdt{};// n-th entry is the index of first update of the n-th day
bool _request();
+ void drawPopUp();
};
-#endif
\ No newline at end of file
+#endif
diff --git a/include/apps/_experiments/OswAppWeatherEncoder.h b/include/apps/_experiments/OswAppWeatherEncoder.h
index 9512cf8f0..fd7344806 100644
--- a/include/apps/_experiments/OswAppWeatherEncoder.h
+++ b/include/apps/_experiments/OswAppWeatherEncoder.h
@@ -1,5 +1,8 @@
#pragma once
#ifdef OSW_FEATURE_WEATHER
+
+#include
+
#include "apps/_experiments/OswAppWeather.h"
class OswAppWeatherEncoder {
@@ -7,7 +10,7 @@ class OswAppWeatherEncoder {
OswAppWeatherEncoder();
bool setUpdate(OswAppWeather::weather_update_t update);
bool setTimestamp(time_t t);
- String getEncoded();
+ std::optional getEncoded();
private:
String _time2str(time_t time);
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 1c15ab6ff..3486d3ef5 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
-Wdouble-promotion
@@ -79,6 +82,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
@@ -93,6 +97,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/apps/_experiments/OswAppWeather.cpp b/src/apps/_experiments/OswAppWeather.cpp
index b813a3f3e..639a30344 100644
--- a/src/apps/_experiments/OswAppWeather.cpp
+++ b/src/apps/_experiments/OswAppWeather.cpp
@@ -3,16 +3,20 @@
#include "apps/_experiments/OswAppWeatherEncoder.h"
#include "services/OswServiceTaskWiFi.h"
#include
-#include
#include
#include
#include
#include
#include "fonts/DS_DIGI12pt7b.h"
-#include "ArduinoJson.h"
-
+#ifndef OSW_EMULATOR
+#include
+#endif
#define OPENWEATHERMAP_URL "https://api.openweathermap.org/data/2.5/forecast?q="
#define URL_REQ OPENWEATHERMAP_URL OPENWEATHERMAP_CITY "," OPENWEATHERMAP_STATE_CODE "&appid=" OPENWEATHERMAP_APIKEY "&cnt=24"
+#ifdef OSW_EMULATOR
+#include
+#include
+#endif
/*
TODO: multiple location support
measurement unit conversion (?)
@@ -20,24 +24,9 @@
-class WeatherDecoder {
- public:
- WeatherDecoder(const String& input_String);
- bool strIsValid();
- time_t getTime();
- std::vector getUpdates();
- private:
- time_t _str2time(const String& t);
- int _str2temp(const String& temp);
- int _str2hum(const String& humidity);
- int _str2pres(const String& pressure);
- int _str2wthr(const String& weather);
- bool in_ok = true;
- int nUpdates = 0;
- String in_String;
-};
-
-WeatherDecoder::WeatherDecoder(const String& input_String) {
+
+
+OswAppWeather::WeatherDecoder::WeatherDecoder(const String& input_String) {
if(input_String.length() < 16 || (input_String.length()%8)!= 0 ) {
this->in_ok = false;
}
@@ -45,17 +34,17 @@ WeatherDecoder::WeatherDecoder(const String& input_String) {
this->nUpdates = (this->in_String.length()-8)/8;
}
-bool WeatherDecoder::strIsValid() {
+bool OswAppWeather::WeatherDecoder::strIsValid() {
return in_ok;
}
-time_t WeatherDecoder::getTime() {
+time_t OswAppWeather::WeatherDecoder::getTime() {
String time_str = this->in_String.substring(0,8);
time_t t = this->_str2time(time_str);
return t;
}
-std::vector WeatherDecoder::getUpdates() {
+std::vector OswAppWeather::WeatherDecoder::getUpdates() {
OswAppWeather::weather_update_t update;
String update_str;
std::vector updates;
@@ -70,78 +59,47 @@ std::vector WeatherDecoder::getUpdates() {
return updates;
}
-time_t WeatherDecoder::_str2time(const String& t) {
+time_t OswAppWeather::WeatherDecoder::_str2time(const String& t) {
int time = t.toInt();
time = time * 8;
time = 2147483647 - time;
return time;
}
-int WeatherDecoder::_str2temp(const String& temp) {
+int OswAppWeather::WeatherDecoder::_str2temp(const String& temp) {
int temp_int = temp.toInt();
return temp_int;
}
-int WeatherDecoder::_str2hum(const String& humidity) {
+int OswAppWeather::WeatherDecoder::_str2hum(const String& humidity) {
int hum = humidity.toInt();
hum = (hum*10)+ 5;
return hum;
}
-int WeatherDecoder::_str2pres(const String& pressure) {
+int OswAppWeather::WeatherDecoder::_str2pres(const String& pressure) {
int pres = pressure.toInt();
pres = pres + 850;
return pres;
}
-int WeatherDecoder::_str2wthr(const String& weather) {
+int OswAppWeather::WeatherDecoder::_str2wthr(const String& weather) {
char wthr = weather[0];
return wthr - 65;
}
+OswAppWeather::WeatherParser::WeatherParser() {}
-
-class WeatherParser {
- public:
- WeatherParser();
- String encodeWeather(DynamicJsonDocument& doc);
- private:
- int _getWCond(int weather_code);
- int cnt;
- std::vector updates;
- std::vectorclearCode{800};//0
- std::vectorcloudsMin{801};//1
- std::vectorcloudsMed{802};//2
- std::vectorcloudsHigh{803, 804};//3
- std::vectormist{701};//4
- std::vectorfog{741};//5
- std::vectorsnowMin{611, 612, 615, 616};//6
- std::vectorsnowMed{600, 613, 601, 620};//7
- std::vectorsnowHigh{602, 621, 622};//8
- std::vectorrainMin{500, 300, 301, 302, 310, 311, 312, 313, 314, 321};//9
- std::vectorrainMed{502, 501};//10
- std::vectorrainHigh{503, 504, 511, 520, 521, 522, 531};//11
- std::vectorthunderstorm{200, 201, 210, 211, 231, 230};//12
- std::vectorthunderstorHeavy{202, 212, 221, 232};//13
- std::vectorsquallTornado{771, 781};//14
- //15 ->unknown
- std::vector>weather_conditions{clearCode, cloudsMin, cloudsMed, cloudsHigh, mist, fog, snowMin, snowMed,
- snowHigh, rainMin, rainMed, rainHigh, thunderstorm,
- thunderstorHeavy, squallTornado };
-};
-
-WeatherParser::WeatherParser() {}
-
-String WeatherParser::encodeWeather(DynamicJsonDocument& doc) {
+std::optional OswAppWeather::WeatherParser::encodeWeather(DynamicJsonDocument& doc) {
const char* code = nullptr;
code = doc["cod"];
if(strcmp("200",code)) {
if(code==nullptr) {
OSW_LOG_E("Error, corrupted API response.");
- return "ERROR_CORRUPTED_RESPONSE";
+ return std::nullopt; // ERROR_CORRUPTED_RESPONSE
} else {
OSW_LOG_E("Error, response code: ", code);
- return "ERROR_API_RESPONSE";
+ return std::nullopt; // ERROR_API_RESPONSE
}
}
cnt = doc["cnt"];
@@ -159,15 +117,16 @@ String WeatherParser::encodeWeather(DynamicJsonDocument& doc) {
update.weather = this->_getWCond(doc["list"][i]["weather"][0]["id"]);
res = encoder.setUpdate(update);
if (!res) {
+ OSW_LOG_E("Failed to pass update to encoder!");
return "ERROR_INPUT";
}
}
return encoder.getEncoded();
}
-int WeatherParser::_getWCond(int weather_code) {
+int OswAppWeather::WeatherParser::_getWCond(int weather_code) {
for(int i=0; i<15; i++) {
- for(int j=0; j < weather_conditions[i].size(); j++) {
+ for(size_t j=0; j < weather_conditions[i].size(); j++) {
if(weather_code == weather_conditions[i][j]) {
return i;
}
@@ -176,7 +135,22 @@ int WeatherParser::_getWCond(int weather_code) {
return 15; // unknown weather def
}
+void OswAppWeather::drawPopUp() {
+ this->hal->gfx()->drawThickLine(50,120,190,120,15,rgb888(255,255,255),true);
+ this->hal->gfx()->drawThickLine(51,120,189,120,14,rgb888(0,0,0),true);
+ this->hal->gfx()->setTextCursor(120,120);
+ this->hal->gfx()->setTextColor(rgb888(255,255,255));
+ this->hal->gfx()->setTextCenterAligned();
+ this->hal->gfx()->setTextMiddleAligned();
+ this->hal->gfx()->print("connecting...");
+}
+
+
void OswAppWeather::drawWeather() {
+ if(this->updtSelector >= this->forecast.size()) {
+ return;
+ }
+
updtTime = initTimestamp + (this->updtSelector * 10800 );
this->printWIcon.drawIcon(this->forecast[this->updtSelector].weather,120,45,1);
//draw time of current weather updatea
@@ -252,7 +226,7 @@ void OswAppWeather::printLastUpdate() {
this->hal->gfx()->setTextCursor(120, 225);
this->hal->gfx()->print(this->initTimeDMY);
}
-
+#ifndef OSW_EMULATOR
void OswAppWeather::weatherRequest() {
if(!OswServiceAllTasks::wifi.isConnected()) {
OswServiceAllTasks::wifi.enableWiFi();
@@ -262,53 +236,69 @@ void OswAppWeather::weatherRequest() {
}
bool OswAppWeather::_request() {
+ // TODO cleanup the error handling (especially the return false; statements)
HTTPClient http;
- OSW_LOG_I(this->url);
- http.begin(this->url, OPENWEATHERMAP_CA_CERT);
int code = 0;
+ OSW_LOG_D("Request: ");
+ OSW_LOG_D(this->url);
+ http.begin(this->url, OPENWEATHERMAP_CA_CERT);
if (OswServiceAllTasks::wifi.isConnected()) {
OswHal::getInstance()->disableDisplayBuffer();
this->forecast.clear();
- this->dataLoaded=false;
- OSW_LOG_I("free heap ", ESP.getFreeHeap());
+ this->dataLoaded = false;
+ OSW_LOG_D("free heap ", ESP.getFreeHeap());
code = http.GET();
} else {
return false;
}
http.end();
OswServiceAllTasks::wifi.disconnectWiFi();
- OSW_LOG_I("code:", code);
- if (code == 200) {
- DynamicJsonDocument doc(16432);
- deserializeJson(doc,http.getStream());
- WeatherParser pars;
- String encoded = pars.encodeWeather(doc);
- int encoded_len = encoded.length();
- char encoded_arr[encoded_len + 1];
- strcpy(encoded_arr, encoded.c_str());
- String encoded_S = String(encoded_arr);
- if (!this->pref.putString("wtr",encoded_S)) {
- OSW_LOG_E("Error: unable to write to NVS");
- this->dataLoaded = false;
- return false;
- }
- } else {
- OSW_LOG_E("Error: API response: ", code);
+ OSW_LOG_D("Request returned code: ", code);
+ if (code != HTTP_CODE_OK) {
+ OSW_LOG_E("Unexpected API response: ", code);
+ this->dataLoaded = false;
+ return false;
+ }
+ DynamicJsonDocument doc(16432);
+ deserializeJson(doc,http.getStream());
+ WeatherParser pars;
+ std::optional encoded = pars.encodeWeather(doc);
+ if(!encoded.has_value()) {
+ OSW_LOG_E("Something went wrong with the encoding of the weather data?!");
+ this->dataLoaded = false;
+ return false;
+ }
+ int encoded_len = encoded.value().length();
+ char encoded_arr[encoded_len + 1];
+ strcpy(encoded_arr, encoded.value().c_str());
+ String encoded_S = String(encoded_arr);
+ if (!this->pref.putString("wtr",encoded_S)) {
+ OSW_LOG_E("Unable to write weather-update to NVS?!");
this->dataLoaded = false;
return false;
}
this->requestMode=false;
bool res = this->loadData();
- if (res) {
- OSW_LOG_I("weather updated correctly");
- this->dataLoaded=true;
- return true;
- } else {
+ if (!res) {
this->dataLoaded = false;
return false;
}
-
+ OSW_LOG_D("weather updated correctly");
+ this->dataLoaded=true;
+ return true;
+}
+#else
+void OswAppWeather::weatherRequest() {
+ this->requestMode = true;
+}
+bool OswAppWeather::_request() {
+ // in emulator instantly "fullfill" the request
+ this->requestMode=false;
+ this->dataLoaded=true;
+ return true;
}
+#endif
+
void OswAppWeather::getDayList(int nUpdates) {
time_t timestamp = this->initTimestamp;
@@ -379,9 +369,36 @@ void OswAppWeather::printDate() {
}
bool OswAppWeather::loadData() {
- String wstr = this->pref.getString("wtr");
- if (!wstr.equals("")) {
- OSW_LOG_I("size of wstr: ", wstr.length());
+ String wstr;
+#ifdef OSW_EMULATOR
+ std::ifstream inFile;
+ inFile.open("file_weather.json"); //open the input file
+ if(!inFile.is_open()) {
+ OSW_LOG_E("Unable to open 'file_weather.json' in the current working directory");
+ OSW_LOG_W("→ Because the emulator has no HTTP-capabilities (yet), you need to provide this file manually for now.");
+ } else {
+ std::stringstream strStream;
+ strStream << inFile.rdbuf();
+ std::string strW = strStream.str();
+ OSW_LOG_D("json file raw:");
+ OSW_LOG_D(strW);
+ DynamicJsonDocument doc(16432*2);// when in emulator more space is needed
+ deserializeJson(doc,strW);
+ WeatherParser pars;
+ std::optional encoded = pars.encodeWeather(doc);
+ if(encoded.has_value()) {
+ OSW_LOG_D("as encoded:");
+ OSW_LOG_D(encoded.value());
+ wstr += encoded.value(); // this copies the value of "encoded" to "wstr" (just assignment does not take ownership of the value itself)
+ } else {
+ OSW_LOG_E("Something went wrong with the encoding of the weather data from the local file?!");
+ }
+ }
+#else
+ wstr = this->pref.getString("wtr");
+#endif
+ if (wstr!="") {
+ OSW_LOG_D("size of wstr: ", wstr.length());
if( (wstr.length() % 8) != 0 ) {
this->dataLoaded = false;
return false;
@@ -393,11 +410,11 @@ bool OswAppWeather::loadData() {
WeatherDecoder decoder(wstr.c_str());
this->initTimestamp = decoder.getTime();
this->getDayList();
- if(strftime(this->initTimeDMY, sizeof(this->initTimeDMY), "%d/%m/%Y", localtime(&this->initTimestamp))) {
- //OSW_LOG_I(this->inittimeDMY);
+ if(!strftime(this->initTimeDMY, sizeof(this->initTimeDMY), "%d/%m/%Y", localtime(&this->initTimestamp))) {
+ OSW_LOG_W("Failed to parse timestamp: ", this->initTimeDMY);
}
- if(strftime(this->initTimeMD, sizeof(this->initTimeMD), "%d/%m", localtime(&this->initTimestamp))) {
- //OSW_LOG_I(this->initTimeMD);
+ if(!strftime(this->initTimeMD, sizeof(this->initTimeMD), "%d/%m", localtime(&this->initTimestamp))) {
+ OSW_LOG_W("Failed to parse timestamp: ", this->initTimeMD);
}
tmInit = localtime(&this->initTimestamp);
forecast = decoder.getUpdates();
@@ -411,7 +428,7 @@ bool OswAppWeather::loadData() {
}
int OswAppWeather::getNextDay() {
- for(int i=0; idayFirstUpdt.size(); i++) {
+ for(size_t i=0; idayFirstUpdt.size(); i++) {
if(this->dayFirstUpdt[i] > this->updtSelector) {
return this->dayFirstUpdt[i];
}
@@ -430,19 +447,23 @@ int OswAppWeather::getPrevDay() {
void OswAppWeather::setup() {
+ OSW_LOG_D("OSW Weather Setup ");
this->location1 = OswConfigAllKeys::weatherLocation1.get();
this->state1 = OswConfigAllKeys::weatherState1.get();
this->api_key = OswConfigAllKeys::weatherApiKey.get();
this->url = String(OPENWEATHERMAP_URL);
- this->url.concat(this->location1);
- this->url.concat(",");
- this->url.concat(this->state1);
- this->url.concat("&appid=");
- this->url.concat(this->api_key);
- this->url.concat("&cnt=24");
- pref.begin("wheater-app");
+ this->url = this->url + this->location1;
+ this->url = this->url + ",";
+ this->url = this->url + this->state1;
+ this->url = this->url + "&appid=";
+ this->url = this->url + this->api_key;
+ this->url = this->url + "&cnt=24";
+ pref.begin("wheater-app", false);
this->loadData();
+ this->hal = OswHal::getInstance(); // ALWAYS update cached hal reference, as it may change at any point (strictly speaking, it should be updated in every loop, but this is a good place to start with)
+ // ↑ this is fixed in OswAppV2 where you always have the up-to-date hal reference available
this->printWIcon.getHal(this->hal);
+ OSW_LOG_D("Setup end");
}
void OswAppWeather::loop() {
@@ -453,19 +474,21 @@ void OswAppWeather::loop() {
this->printDate();
this->printLastUpdate();
}
+
+#ifndef OSW_EMULATOR
if(this->requestMode) {
if (OswServiceAllTasks::wifi.isConnected()) {
this->_request();
- } else {//pop-up
- this->hal->gfx()->drawThickLine(50,120,190,120,15,rgb888(255,255,255),true);
- this->hal->gfx()->drawThickLine(51,120,189,120,14,rgb888(0,0,0),true);
- this->hal->gfx()->setTextCursor(120,120);
- this->hal->gfx()->setTextColor(rgb888(255,255,255));
- this->hal->gfx()->setTextCenterAligned();
- this->hal->gfx()->setTextMiddleAligned();
- this->hal->gfx()->print("connecting...");
+ } else {
+ this->drawPopUp();
}
}
+#else
+ if(this->requestMode) {
+ this->_request();
+ this->drawPopUp();
+ }
+#endif
if (hal->btnHasGoneDown(BUTTON_2)) {
if(this->mainSelector==1) { // next update
if(this->updtSelector<23) {
@@ -473,7 +496,7 @@ void OswAppWeather::loop() {
}
}
if(this->mainSelector==0) { //next day
- if(this->updtSelector >= this->dayFirstUpdt[this->dayFirstUpdt.size()-1]) {
+ if(this->dayFirstUpdt.size() > 0 and this->updtSelector >= this->dayFirstUpdt[this->dayFirstUpdt.size()-1]) {
this->updtSelector=this->updtSelector;
} else {
this->updtSelector = this->getNextDay();
@@ -511,4 +534,4 @@ void OswAppWeather::loop() {
void OswAppWeather::stop() {
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/apps/_experiments/OswAppWeatherEncoder.cpp b/src/apps/_experiments/OswAppWeatherEncoder.cpp
index cb491393a..ac6257dd0 100644
--- a/src/apps/_experiments/OswAppWeatherEncoder.cpp
+++ b/src/apps/_experiments/OswAppWeatherEncoder.cpp
@@ -6,15 +6,19 @@ OswAppWeatherEncoder::OswAppWeatherEncoder() {}
bool OswAppWeatherEncoder::setUpdate(OswAppWeather::weather_update_t update) {
bool update_ok = true;
if(update.temp > 99 || update.temp < -99 ) {
+ OSW_LOG_W("Invalid TEMPERATURE: ", update.temp);
update_ok = false;
}
if(update.humidity > 100 || update.humidity < 0) {
+ OSW_LOG_W("Invalid HUMIDITY: ", update.humidity);
update_ok = false;
}
if(update.pressure < 0 || update.pressure > 2000 ) {
+ OSW_LOG_W("Invalid PRESSURE: ", update.pressure);
update_ok = false;
}
if(update.weather < 0 || update.weather > 15) {
+ OSW_LOG_W("Invalid WEATHER: ", update.weather);
update_ok = false;
}
if(!update_ok) {
@@ -39,14 +43,14 @@ bool OswAppWeatherEncoder::setTimestamp(time_t t) {
return false;
}
-String OswAppWeatherEncoder::getEncoded() {
+std::optional OswAppWeatherEncoder::getEncoded() {
if(this->time_loaded) {
String encoded;
encoded += _time2str(this->timestamp);
encoded += this->updates;
return encoded;
} else {
- return "error_no_timestamp";
+ return std::nullopt; // error_no_timestamp
}
}
diff --git a/src/main.cpp b/src/main.cpp
index ea794cadb..32155d6b7 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -207,7 +207,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