diff --git a/.github/actions/install-build/action.yml b/.github/actions/install-build/action.yml new file mode 100644 index 0000000..891a8a5 --- /dev/null +++ b/.github/actions/install-build/action.yml @@ -0,0 +1,43 @@ +name: "Install and build" +description: "Install and build" + +runs: + using: "composite" + steps: + - uses: actions/setup-node@v4 + with: + node-version: lts/* + cache: yarn + cache-dependency-path: '**/yarn.lock' + - uses: actions/cache@v3 + with: + path: | + ~/.cache/pip + ~/.platformio/.cache + ~/data/node_modules + key: ${{ runner.os }}-pio + - uses: actions/setup-python@v4 + with: + python-version: '3.9' + - name: Get current date + id: dateAndTime + shell: bash + run: echo "dateAndTime=$(date +'%Y-%m-%d-%H:%M')" >> $GITHUB_OUTPUT + - name: Install PlatformIO Core + shell: bash + run: pip install --upgrade platformio + # - name: Run unit tests + # shell: bash + # run: mkdir -p junit-reports && pio test -e native_test_only --junit-output-path junit-reports/ + # - name: Publish Test Report + # uses: mikepenz/action-junit-report@v4 + # if: success() || failure() # always run even if the previous step fails + # with: + # report_paths: '**/junit-reports/*.xml' + # detailed_summary: true + - name: Build BTClock firmware + shell: bash + run: pio run + - name: Build BTClock filesystem + shell: bash + run: pio run --target buildfs \ No newline at end of file diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 0000000..209c417 --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,19 @@ +name: Pull Request Workflow + +on: + pull_request: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write + checks: write + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: "Install and build" + uses: ./.github/actions/install-build \ No newline at end of file diff --git a/.github/workflows/tagging.yml b/.github/workflows/tagging.yml new file mode 100644 index 0000000..decbf59 --- /dev/null +++ b/.github/workflows/tagging.yml @@ -0,0 +1,71 @@ +name: BTClock CI + +on: + push: + tags: + - '*' + +jobs: + build: + strategy: + matrix: + epd_variant: [213epd, 29epd] + chip: + - name: lolin_s2_mini + version: esp32s2 + # chips: + # - name: lolin_s3_mini + # version: esp32s3 + runs-on: ubuntu-latest + permissions: + contents: write + checks: write + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: "Install and build" + uses: ./.github/actions/install-build + + - name: Install esptools.py + run: pip install --upgrade esptool + + - name: Create merged firmware binary + run: mkdir -p ${{ matrix.chip.name }}_${{ matrix.epd_variant }} && esptool.py --chip ${{ matrix.chips.version }} merge_bin -o ${{ matrix.chip.name }}_${{ matrix.epd_variant }}/${{ matrix.chip.name }}_${{ matrix.epd_variant }}.bin --flash_mode dio 0x0000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/bootloader.bin 0x8000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/partitions.bin 0xe000 ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin 0x10000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/firmware.bin 0x369000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/littlefs.bin + + - name: Create checksum for merged binary + run: shasum -a 256 ${{ matrix.chip.name }}_${{ matrix.epd_variant }}/${{ matrix.chip.name }}_${{ matrix.epd_variant }}.bin | awk '{print $1}' > $${{ matrix.chip.name }}_${{ matrix.epd_variant }}/${{ matrix.chip.name }}_${{ matrix.epd_variant }}.sha256 + + # - name: Write commit hash to file + # run: echo $GITHUB_SHA > ${{ matrix.chip.name }}_${{ matrix.epd_variant }}/commit.txt + + # - name: Write build date to file + # run: echo "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" > ${{ matrix.chip.name }}_${{ matrix.epd_variant }}/date.txt + + # - name: Copy all artifacts to output folder + # run: cp .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/*.bin ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin ${{ matrix.chip.name }}_${{ matrix.epd_variant }} + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: build-${{ matrix.chip.name }}-${{ matrix.epd_variant }} + path: | + ${{ matrix.chip.name }}_${{ matrix.epd_variant }}/*.bin + ${{ matrix.chip.name }}_${{ matrix.epd_variant }}/*.sha256 + release: + runs-on: ubuntu-latest + permissions: + contents: write + checks: write + needs: build + - name: Download matrix outputs + uses: actions/download-artifact@v4 + with: + name: build-* + - name: Create release + uses: ncipollo/release-action@v1 + with: + artifacts: "*/*.bin,*/*.sha256" + allowUpdates: true + removeArtifacts: true + makeLatest: true \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index b57b2ac..fa6c962 100644 --- a/platformio.ini +++ b/platformio.ini @@ -2,7 +2,7 @@ ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags -; Library options: dependencies, extra library storages +; Library options: dependencies, extra library storages tl ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples @@ -10,6 +10,7 @@ [platformio] data_dir = data/build +default_envs = lolin_s2_mini_213epd, lolin_s2_mini_29epd, lolin_s3_mini_213epd [btclock_base] platform = espressif32 @@ -30,16 +31,30 @@ lib_deps = extends = btclock_base board = lolin_s2_mini +[env:lolin_s3_mini] +extends = btclock_base +board = lolin_s3_mini + [env:lolin_s2_mini_213epd] extends = env:lolin_s2_mini -board = lolin_s2_mini build_flags = ${btclock_base.build_flags} -D VERSION_EPD_2_13 [env:lolin_s2_mini_29epd] extends = env:lolin_s2_mini -board = lolin_s2_mini +build_flags = + ${btclock_base.build_flags} + -D VERSION_EPD_2_9 + +[env:lolin_s3_mini_213epd] +extends = env:lolin_s3_mini +build_flags = + ${btclock_base.build_flags} + -D VERSION_EPD_2_13 + +[env:lolin_s3_mini_29epd] +extends = env:lolin_s3_mini build_flags = ${btclock_base.build_flags} -D VERSION_EPD_2_9 \ No newline at end of file diff --git a/src/config.cpp b/src/config.cpp index 39ca0e7..0c30984 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -19,6 +19,24 @@ void setupTime() void setupPreferences() { preferences.begin("btclock", false); + + if (!preferences.isKey(SETTING_ROW1_CONTENT)) + { + preferences.putUInt(SETTING_ROW1_CONTENT, LINE_BLOCKHEIGHT); + } + if (!preferences.isKey(SETTING_ROW2_CONTENT)) + { + preferences.putUInt(SETTING_ROW2_CONTENT, LINE_SATSPERUNIT); + } + if (!preferences.isKey(SETTING_ROW3_CONTENT)) + { + preferences.putUInt(SETTING_ROW3_CONTENT, LINE_MEMPOOL_FEES); + } + + if (!preferences.isKey(SETTING_CURRENCY)) + { + preferences.putString(SETTING_CURRENCY, CURRENCY_USD); + } } void setupWifi() diff --git a/src/data.cpp b/src/data.cpp index bfdc07f..b5f7eca 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -6,6 +6,9 @@ const String mempoolPriceApiUrl = mempoolInstance + "/api/v1/prices"; const String mempoolBlockApiUrl = mempoolInstance + "/api/blocks/tip/height"; const String mempoolFeeApiUrl = mempoolInstance + "/api/v1/fees/recommended"; +uint lastPrice; +uint lastBlock; + uint getPrice() { HTTPClient http; @@ -22,10 +25,10 @@ uint getPrice() String payload = http.getString(); JsonDocument doc; deserializeJson(doc, payload); - usdPrice = doc["USD"].as(); - eurPrice = doc["EUR"].as(); - return usdPrice; + lastPrice = doc[preferences.getString(SETTING_CURRENCY)].as(); + + return lastPrice; } else { @@ -36,7 +39,7 @@ uint getPrice() return 0; } -String getBlock() +uint getBlock() { HTTPClient http; @@ -49,9 +52,10 @@ String getBlock() uint usdPrice, eurPrice; if (httpCode == 200) { - String payload = http.getString(); + uint payload = http.getString().toInt(); - return payload; + lastBlock = payload; + return lastBlock; } else { @@ -59,7 +63,7 @@ String getBlock() } http.end(); - return ""; + return 0; } String getMempoolFees() diff --git a/src/data.hpp b/src/data.hpp index 313241a..9b2e363 100644 --- a/src/data.hpp +++ b/src/data.hpp @@ -8,5 +8,5 @@ #include "shared.hpp" uint getPrice(); -String getBlock(); +uint getBlock(); String getMempoolFees(); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 5bf1518..e6e4a87 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -89,7 +89,7 @@ void loop() } } - String block = getBlock(); + String block = String(getBlock()); uint tryCount = 0; while (block.equals("")) diff --git a/src/shared.hpp b/src/shared.hpp index f405c4d..52ef439 100644 --- a/src/shared.hpp +++ b/src/shared.hpp @@ -56,13 +56,28 @@ #define ICON_CHECK "Q" #define ICON_WARNING "R" +#define SETTING_ROW1_CONTENT "row1" +#define SETTING_ROW2_CONTENT "row2" +#define SETTING_ROW3_CONTENT "row3" +#define SETTING_CURRENCY "currency" + const int LINE_BLOCKHEIGHT = 0; -const int LINE_HALVING_COUNTDOWN = 1; -const int LINE_SATSPERDOLLAR = 2; -const int LINE_FIATPRICE = 3; -const int LINE_MEMPOOL_FEES = 4; -const int LINE_TIME = 5; -const int LINE_DATE = 6; +const int LINE_MEMPOOL_FEES = 1; +const int LINE_MEMPOOL_FEES_MEDIAN = 2; +const int LINE_HALVING_COUNTDOWN = 10; +const int LINE_SATSPERUNIT = 20; +const int LINE_FIATPRICE = 30; +const int LINE_MARKETCAP = 40; +const int LINE_TIME = 99; +const int LINE_DATE = 100; + +#define CURRENCY_USD "USD" +#define CURRENCY_EUR "EUR" +#define CURRENCY_GBP "GBP" +#define CURRENCY_CAD "CAD" +#define CURRENCY_CHF "CHF" +#define CURRENCY_AUD "AUD" +#define CURRENCY_JPY "JPY" extern WiFiClientSecure client; extern GxEPD2_BW display; diff --git a/src/webserver.cpp b/src/webserver.cpp index c06d3b2..7d31f7f 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -2,12 +2,20 @@ #include AsyncWebServer server(80); +String uintSettings[] = {SETTING_ROW1_CONTENT, SETTING_ROW2_CONTENT, SETTING_ROW3_CONTENT}; -void setupWebserver() { - if (!LittleFS.begin(true)) { +void setupWebserver() +{ + if (!LittleFS.begin(true)) + { Serial.println(F("An Error has occurred while mounting LittleFS")); } + server.on("/api/settings", HTTP_GET, onApiSettingsGet); + AsyncCallbackJsonWebHandler *settingsPatchHandler = + new AsyncCallbackJsonWebHandler("/api/json/settings", onApiSettingsPatch); + server.addHandler(settingsPatchHandler); + server.serveStatic("/build", LittleFS, "/build"); server.on("/", HTTP_GET, onIndex); @@ -20,17 +28,54 @@ void setupWebserver() { server.begin(); } -void onIndex(AsyncWebServerRequest *request) { +void onApiSettingsGet(AsyncWebServerRequest *request) +{ + JsonDocument root; + for (String setting : uintSettings) + { + root[setting] = preferences.getUInt(setting.c_str()); + } + + AsyncResponseStream *response = + request->beginResponseStream("application/json"); + serializeJson(root, *response); + + request->send(response); +} + +void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json) +{ + JsonObject settings = json.as(); + + for (String setting : uintSettings) + { + if (settings.containsKey(setting)) + { + preferences.putUInt(setting.c_str(), settings[setting].as()); + Serial.printf("Setting %s to %d\r\n", setting.c_str(), + settings[setting].as()); + } + } + + request->send(200); +} + +void onIndex(AsyncWebServerRequest *request) +{ request->send(LittleFS, "/index.html", String(), false); } -void onNotFound(AsyncWebServerRequest *request) { - if (request->method() == HTTP_OPTIONS || - request->hasHeader("Sec-Fetch-Mode")) { +void onNotFound(AsyncWebServerRequest *request) +{ + if (request->method() == HTTP_OPTIONS || + request->hasHeader("Sec-Fetch-Mode")) + { // Serial.printf("NotFound, Return[%d]\n", 200); request->send(200); - } else { + } + else + { // Serial.printf("NotFound, Return[%d]\n", 404); request->send(404); } diff --git a/src/webserver.hpp b/src/webserver.hpp index 653b62b..7ca57cc 100644 --- a/src/webserver.hpp +++ b/src/webserver.hpp @@ -2,8 +2,15 @@ // Keep order of includes because of conflicts #include "ESPAsyncWebServer.h" +#include "AsyncJson.h" + #include +#include void setupWebserver(); + +void onApiSettingsGet(AsyncWebServerRequest *request); +void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json); + void onIndex(AsyncWebServerRequest *request); void onNotFound(AsyncWebServerRequest *request); \ No newline at end of file