From 71486da9c740a3c4dfb6502b5f2298ce2d5e0fe9 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sat, 16 Mar 2024 23:59:03 +0100 Subject: [PATCH 01/22] Fix artifact sharing --- .github/workflows/tagging.yml | 5 +++-- src/webserver.cpp | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tagging.yml b/.github/workflows/tagging.yml index 82fb7a0..a7e1035 100644 --- a/.github/workflows/tagging.yml +++ b/.github/workflows/tagging.yml @@ -22,8 +22,9 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@v4 with: + retention-days: 1 name: build-outputs - path: .pio + path: .pio/**/*.bin build: needs: prepare @@ -48,7 +49,7 @@ jobs: - 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.chip.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 ~/.pio/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 + run: mkdir -p ${{ matrix.chip.name }}_${{ matrix.epd_variant }} && esptool.py --chip ${{ matrix.chip.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 .pio/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 diff --git a/src/webserver.cpp b/src/webserver.cpp index 7d31f7f..590b1bf 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -2,7 +2,9 @@ #include AsyncWebServer server(80); -String uintSettings[] = {SETTING_ROW1_CONTENT, SETTING_ROW2_CONTENT, SETTING_ROW3_CONTENT}; +const String uintSettings[] = {SETTING_ROW1_CONTENT, SETTING_ROW2_CONTENT, SETTING_ROW3_CONTENT}; +const String stringSettings[] = {SETTING_CURRENCY}; +const String boolSettings[] = {}; void setupWebserver() { @@ -36,6 +38,16 @@ void onApiSettingsGet(AsyncWebServerRequest *request) root[setting] = preferences.getUInt(setting.c_str()); } + for (String setting : stringSettings) + { + root[setting] = preferences.getString(setting.c_str()); + } + + for (String setting : boolSettings) + { + root[setting] = preferences.getBool(setting.c_str()); + } + AsyncResponseStream *response = request->beginResponseStream("application/json"); serializeJson(root, *response); @@ -57,6 +69,26 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json) } } + for (String setting : stringSettings) + { + if (settings.containsKey(setting)) + { + preferences.putString(setting.c_str(), settings[setting].as()); + Serial.printf("Setting %s to %s\r\n", setting.c_str(), + settings[setting].as()); + } + } + + for (String setting : boolSettings) + { + if (settings.containsKey(setting)) + { + preferences.putBool(setting.c_str(), settings[setting].as()); + Serial.printf("Setting %s to %d\r\n", setting.c_str(), + settings[setting].as()); + } + } + request->send(200); } From c5c7791c330b367bcd09665fda6724bd151cac94 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sun, 17 Mar 2024 00:12:33 +0100 Subject: [PATCH 02/22] Fix OTA update and enable S3 mini target --- .github/workflows/tagging.yml | 8 ++++---- src/main.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tagging.yml b/.github/workflows/tagging.yml index a7e1035..8d50475 100644 --- a/.github/workflows/tagging.yml +++ b/.github/workflows/tagging.yml @@ -34,9 +34,8 @@ jobs: chip: - name: lolin_s2_mini version: esp32s2 - # chips: - # - name: lolin_s3_mini - # version: esp32s3 + - name: lolin_s3_mini + version: esp32s3 runs-on: ubuntu-latest permissions: contents: write @@ -80,7 +79,8 @@ jobs: - name: Download matrix outputs uses: actions/download-artifact@v4 with: - name: build-* + pattern: build-* + merge-multiple: true - name: Create release uses: ncipollo/release-action@v1 with: diff --git a/src/main.cpp b/src/main.cpp index e6e4a87..b1ef4f4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,8 +53,8 @@ void setup() void loop() { + ArduinoOTA.handle(); if (isUpdating) { - ArduinoOTA.handle(); return; } From 626877d4ee5ece64976946766cba6ce8a96a158b Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sun, 17 Mar 2024 00:47:59 +0100 Subject: [PATCH 03/22] Working row settings, build all targets --- .github/workflows/tagging.yml | 1 + platformio.ini | 2 +- src/main.cpp | 200 +++++++++++++++++++++------------- src/webserver.cpp | 1 + 4 files changed, 128 insertions(+), 76 deletions(-) diff --git a/.github/workflows/tagging.yml b/.github/workflows/tagging.yml index 8d50475..4021d6e 100644 --- a/.github/workflows/tagging.yml +++ b/.github/workflows/tagging.yml @@ -28,6 +28,7 @@ jobs: build: needs: prepare + continue-on-error: true strategy: matrix: epd_variant: [213epd, 29epd] diff --git a/platformio.ini b/platformio.ini index fa6c962..22f1f4a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,7 +10,7 @@ [platformio] data_dir = data/build -default_envs = lolin_s2_mini_213epd, lolin_s2_mini_29epd, lolin_s3_mini_213epd +default_envs = lolin_s2_mini_213epd, lolin_s2_mini_29epd, lolin_s3_mini_213epd, lolin_s3_mini_29epd [btclock_base] platform = espressif32 diff --git a/src/main.cpp b/src/main.cpp index b1ef4f4..b510d3e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,6 +21,10 @@ GxEPD2_BW display = EPD_CLASS(4, 2, 3, 1); GxEPD2_BW display = EPD_CLASS(5, 3, 2, 1); #endif +typedef void (*MethodPtr)(String); + +MethodPtr methods[] = { nullptr, updateRow1, updateRow2, updateRow3 }; + WiFiClientSecure client; uint currentPrice = 0; String currentBlock = ""; @@ -89,93 +93,139 @@ void loop() } } - String block = String(getBlock()); - uint tryCount = 0; - while (block.equals("")) - { - block = getBlock(); - Serial.print("Retry block.."); - tryCount++; - Serial.println(tryCount); - delay(1000); - - if (tryCount % 5) - { - WiFi.disconnect(); - WiFi.reconnect(); - - while (WiFi.status() != WL_CONNECTED) - { - Serial.print('.'); - delay(1000); + for (uint i = 1; i <= 3; i++) { + String rowContent = ""; + char keyName[5]; + snprintf(keyName, sizeof(keyName), "row%d", i); + Serial.print(keyName); + Serial.print(" "); + Serial.println(preferences.getUInt(keyName)); + switch (preferences.getUInt(keyName)) { + case LINE_BLOCKHEIGHT: + rowContent = getBlock(); + break; + case LINE_MEMPOOL_FEES: + rowContent = getMempoolFees(); + break; + case LINE_MEMPOOL_FEES_MEDIAN: + rowContent = "NOT IMPL"; + break; + case LINE_HALVING_COUNTDOWN: + rowContent = "NOT IMPL"; + break; + case LINE_SATSPERUNIT: { + uint satsPerDollar = int(round(1 / float(getPrice()) * 10e7)); + rowContent = satsPerDollar; + break; } + case LINE_FIATPRICE: + rowContent = getPrice(); + break; + case LINE_MARKETCAP: + rowContent = "NOT IMPL"; + break; + case LINE_TIME: + rowContent = "NOT IMPL"; + break; + case LINE_DATE: + rowContent = "NOT IMPL"; + break; + default: + rowContent = "DEFAULT"; } + + methods[i](rowContent); } - uint price = getPrice(); - tryCount = 0; - while (price == 0) - { - price = getPrice(); - if (Serial.available()) - Serial.print("Retry price.."); - tryCount++; - if (Serial.available()) - Serial.println(tryCount); - delay(1000); - } + // String block = String(getBlock()); - uint satsPerDollar = int(round(1 / float(price) * 10e7)); + // uint tryCount = 0; + // while (block.equals("")) + // { + // block = getBlock(); + // Serial.print("Retry block.."); + // tryCount++; - String mempoolFees = getMempoolFees(); - tryCount = 0; - while (mempoolFees.equals("")) - { - mempoolFees = getMempoolFees(); - Serial.print("Retry mempoolfees.."); - tryCount++; + // Serial.println(tryCount); + // delay(1000); - Serial.println(tryCount); - delay(1000); - } + // if (tryCount % 5) + // { + // WiFi.disconnect(); + // WiFi.reconnect(); - if (!currentFees.equals(mempoolFees)) - { - updateRow1(mempoolFees); - currentFees = mempoolFees; - Serial.print(F("Fees is now ")); - Serial.println(currentFees); - } - else - { - Serial.println(F("No need to update fees")); - } + // while (WiFi.status() != WL_CONNECTED) + // { + // Serial.print('.'); + // delay(1000); + // } + // } + // } - if (price != currentPrice) - { - updateRow3(String(satsPerDollar)); - currentPrice = price; - Serial.print(F("Price is now ")); - Serial.println(currentPrice); - } - else - { - Serial.println(F("No need to update price")); - } + // uint price = getPrice(); + // tryCount = 0; + // while (price == 0) + // { + // price = getPrice(); + // if (Serial.available()) + // Serial.print("Retry price.."); + // tryCount++; + // if (Serial.available()) + // Serial.println(tryCount); + // delay(1000); + // } - if (!block.equals(currentBlock)) - { - updateRow2(block); - currentBlock = block; - Serial.print(F("Block is now ")); - Serial.println(currentBlock); - } - else - { - Serial.println(F("No need to update block")); - } + // uint satsPerDollar = int(round(1 / float(price) * 10e7)); + + // String mempoolFees = getMempoolFees(); + // tryCount = 0; + // while (mempoolFees.equals("")) + // { + // mempoolFees = getMempoolFees(); + // Serial.print("Retry mempoolfees.."); + // tryCount++; + + // Serial.println(tryCount); + // delay(1000); + // } + + // if (!currentFees.equals(mempoolFees)) + // { + // updateRow1(mempoolFees); + // currentFees = mempoolFees; + // Serial.print(F("Fees is now ")); + // Serial.println(currentFees); + // } + // else + // { + // Serial.println(F("No need to update fees")); + // } + + // if (price != currentPrice) + // { + // updateRow3(String(satsPerDollar)); + // currentPrice = price; + // Serial.print(F("Price is now ")); + // Serial.println(currentPrice); + // } + // else + // { + // Serial.println(F("No need to update price")); + // } + + // if (!block.equals(currentBlock)) + // { + // updateRow2(block); + // currentBlock = block; + // Serial.print(F("Block is now ")); + // Serial.println(currentBlock); + // } + // else + // { + // Serial.println(F("No need to update block")); + // } // updateRows(mempoolFees, block, String(price)); diff --git a/src/webserver.cpp b/src/webserver.cpp index 590b1bf..785de57 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -21,6 +21,7 @@ void setupWebserver() server.serveStatic("/build", LittleFS, "/build"); server.on("/", HTTP_GET, onIndex); + server.onNotFound(onNotFound); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Methods", From d46b07399f87a2c9d79d20ddefcd9cb9cb028b48 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sun, 17 Mar 2024 00:59:06 +0100 Subject: [PATCH 04/22] Fix Arduino OTA --- .github/workflows/tagging.yml | 6 +-- src/config.cpp | 21 ++++++++- src/config.hpp | 2 + src/main.cpp | 88 ++++++++++++++++++----------------- 4 files changed, 71 insertions(+), 46 deletions(-) diff --git a/.github/workflows/tagging.yml b/.github/workflows/tagging.yml index 4021d6e..29b4b62 100644 --- a/.github/workflows/tagging.yml +++ b/.github/workflows/tagging.yml @@ -23,7 +23,7 @@ jobs: uses: actions/upload-artifact@v4 with: retention-days: 1 - name: build-outputs + name: prepared-outputs path: .pio/**/*.bin build: @@ -44,7 +44,7 @@ jobs: steps: - uses: actions/download-artifact@v4 with: - name: build-outputs + name: prepared-outputs path: .pio - name: Install esptools.py run: pip install --upgrade esptool @@ -85,7 +85,7 @@ jobs: - name: Create release uses: ncipollo/release-action@v1 with: - artifacts: "*/*.bin,*/*.sha256" + artifacts: "**/*.bin,**/*.sha256" allowUpdates: true removeArtifacts: true makeLatest: true \ No newline at end of file diff --git a/src/config.cpp b/src/config.cpp index 0c30984..9363094 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -5,6 +5,7 @@ Preferences preferences; const char *ntpServer = "pool.ntp.org"; const long gmtOffset_sec = 0; const int daylightOffset_sec = 3600; +TaskHandle_t OTAHandle = NULL; #define STA_SSID "" #define STA_PASS "" @@ -201,4 +202,22 @@ void setupOTA() else if (error == OTA_END_ERROR) Serial.println("End Failed"); }); ArduinoOTA.begin(); -} \ No newline at end of file + + xTaskCreatePinnedToCore( + OTAUpdateTask, // Task function + "OTAUpdateTask", // Task name + 4096, // Stack size + NULL, // Task parameters + 1, // Priority (higher value means higher priority) + &OTAHandle, // Task handle + 0 // Core to run the task (0 or 1) + ); +} + + +void OTAUpdateTask(void *pvParameters) { + for (;;) { + ArduinoOTA.handle(); // Handle OTA updates + vTaskDelay(1000 / portTICK_PERIOD_MS); // Delay to avoid high CPU usage + } +} diff --git a/src/config.hpp b/src/config.hpp index f3bb73d..2def29c 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -15,6 +15,8 @@ void setupTime(); void setupPreferences(); void setupWifi(); void setupOTA(); +void OTAUpdateTask(void *pvParameters); + void wakeModemSleep(); void setModemSleep(); diff --git a/src/main.cpp b/src/main.cpp index b510d3e..049c592 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,7 +23,7 @@ GxEPD2_BW display = EPD_CLASS(5, 3, 2, 1); typedef void (*MethodPtr)(String); -MethodPtr methods[] = { nullptr, updateRow1, updateRow2, updateRow3 }; +MethodPtr methods[] = {nullptr, updateRow1, updateRow2, updateRow3}; WiFiClientSecure client; uint currentPrice = 0; @@ -45,7 +45,7 @@ void setup() setupWifi(); setupTime(); - if (!inPowerSaveMode()) + if (!inPowerSaveMode()) { setupWebserver(); setupOTA(); @@ -57,8 +57,8 @@ void setup() void loop() { - ArduinoOTA.handle(); - if (isUpdating) { + if (isUpdating) + { return; } @@ -93,47 +93,48 @@ void loop() } } - - - for (uint i = 1; i <= 3; i++) { + for (uint i = 1; i <= 3; i++) + { String rowContent = ""; char keyName[5]; snprintf(keyName, sizeof(keyName), "row%d", i); Serial.print(keyName); Serial.print(" "); Serial.println(preferences.getUInt(keyName)); - switch (preferences.getUInt(keyName)) { - case LINE_BLOCKHEIGHT: - rowContent = getBlock(); - break; - case LINE_MEMPOOL_FEES: - rowContent = getMempoolFees(); - break; - case LINE_MEMPOOL_FEES_MEDIAN: - rowContent = "NOT IMPL"; - break; - case LINE_HALVING_COUNTDOWN: - rowContent = "NOT IMPL"; - break; - case LINE_SATSPERUNIT: { - uint satsPerDollar = int(round(1 / float(getPrice()) * 10e7)); - rowContent = satsPerDollar; - break; - } - case LINE_FIATPRICE: - rowContent = getPrice(); - break; - case LINE_MARKETCAP: - rowContent = "NOT IMPL"; - break; - case LINE_TIME: - rowContent = "NOT IMPL"; - break; - case LINE_DATE: - rowContent = "NOT IMPL"; - break; - default: - rowContent = "DEFAULT"; + switch (preferences.getUInt(keyName)) + { + case LINE_BLOCKHEIGHT: + rowContent = getBlock(); + break; + case LINE_MEMPOOL_FEES: + rowContent = getMempoolFees(); + break; + case LINE_MEMPOOL_FEES_MEDIAN: + rowContent = "NOT IMPL"; + break; + case LINE_HALVING_COUNTDOWN: + rowContent = "NOT IMPL"; + break; + case LINE_SATSPERUNIT: + { + uint satsPerDollar = int(round(1 / float(getPrice()) * 10e7)); + rowContent = satsPerDollar; + break; + } + case LINE_FIATPRICE: + rowContent = getPrice(); + break; + case LINE_MARKETCAP: + rowContent = "NOT IMPL"; + break; + case LINE_TIME: + rowContent = "NOT IMPL"; + break; + case LINE_DATE: + rowContent = "NOT IMPL"; + break; + default: + rowContent = "DEFAULT"; } methods[i](rowContent); @@ -231,17 +232,20 @@ void loop() delay(2 * 1000); - if (inPowerSaveMode()) { + if (inPowerSaveMode()) + { display.hibernate(); setModemSleep(); esp_sleep_enable_timer_wakeup(50 * 1000000); esp_light_sleep_start(); display.init(0, false); wakeModemSleep(); - } else { + } + else + { Serial.println(F("Sleeping")); sleep(50); -// delay(50 * 1000); + // delay(50 * 1000); Serial.println(F("Waking up")); } } From 59c9bf4477bd17119ae8f7c2fcb16f20de5b4b55 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sun, 17 Mar 2024 02:15:59 +0100 Subject: [PATCH 05/22] Add build scripts and API calls for WebUI --- platformio.ini | 2 ++ scripts/git_rev.py | 8 ++++++++ src/config.cpp | 6 ++++++ src/config.hpp | 2 +- src/epd.cpp | 36 +++++++++++++++++++++++++++------- src/epd.hpp | 6 +++--- src/main.cpp | 15 +++++++++----- src/shared.cpp | 13 ++++++++++++ src/shared.hpp | 49 ++++++++++++++++++++++++++++------------------ src/webserver.cpp | 37 ++++++++++++++++++++++++++++++++++ src/webserver.hpp | 2 ++ 11 files changed, 141 insertions(+), 35 deletions(-) create mode 100644 scripts/git_rev.py create mode 100644 src/shared.cpp diff --git a/platformio.ini b/platformio.ini index 22f1f4a..643b015 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,6 +20,8 @@ monitor_filters = esp32_exception_decoder, colorize board_build.filesystem = littlefs board_build.partitions = partition.csv build_flags = + !python scripts/git_rev.py + -DLAST_BUILD_TIME=$UNIX_TIME lib_deps = zinggjm/GxEPD2@^1.5.6 https://github.com/tzapu/WiFiManager.git#v2.0.17 diff --git a/scripts/git_rev.py b/scripts/git_rev.py new file mode 100644 index 0000000..9594475 --- /dev/null +++ b/scripts/git_rev.py @@ -0,0 +1,8 @@ +import subprocess + +revision = ( + subprocess.check_output(["git", "rev-parse", "HEAD"]) + .strip() + .decode("utf-8") +) +print("'-DGIT_REV=\"%s\"'" % revision) \ No newline at end of file diff --git a/src/config.cpp b/src/config.cpp index 9363094..65ad555 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -38,6 +38,11 @@ void setupPreferences() { preferences.putString(SETTING_CURRENCY, CURRENCY_USD); } + + if (!preferences.isKey(SETTING_HOSTNAME_PREFIX)) + { + preferences.putString(SETTING_HOSTNAME_PREFIX, "oc"); + } } void setupWifi() @@ -221,3 +226,4 @@ void OTAUpdateTask(void *pvParameters) { vTaskDelay(1000 / portTICK_PERIOD_MS); // Delay to avoid high CPU usage } } + diff --git a/src/config.hpp b/src/config.hpp index 2def29c..1312aa6 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -20,4 +20,4 @@ void OTAUpdateTask(void *pvParameters); void wakeModemSleep(); void setModemSleep(); -bool inPowerSaveMode(); \ No newline at end of file +bool inPowerSaveMode(); diff --git a/src/epd.cpp b/src/epd.cpp index 9552f3c..10ad8d0 100644 --- a/src/epd.cpp +++ b/src/epd.cpp @@ -9,6 +9,10 @@ String currentRow1 = ""; String currentRow2 = ""; String currentRow3 = ""; +char currentIcon1; +char currentIcon2; +char currentIcon3; + void setupDisplay() { display.init(0, true); @@ -60,8 +64,11 @@ void setupDisplay() // display.display(true); } -void updateRow2(String c) +void updateRow2(String c, char icon) { + if (c.equals(currentRow2) && icon == currentIcon2) + return; + display.setRotation(1); display.setFont(&ROW2_FONT); display.setTextColor(GxEPD_BLACK); @@ -82,17 +89,23 @@ void updateRow2(String c) display.setFont(&ROW2_ICONFONT); display.setCursor(x, y); - display.print(ICON_BLOCK); + display.print(icon); display.setFont(&ROW2_FONT); display.setCursor(x + ROW2_ICONWIDTH, y); display.print(c); } while (display.nextPage()); // display.display(true); + + currentRow2 = c; + currentIcon2 = icon; } -void updateRow3(String c) +void updateRow3(String c, char icon) { + if (c.equals(currentRow3) && icon == currentIcon3) + return; + display.setRotation(1); display.setFont(&LibreFranklin_SemiBold15pt7b); display.setTextColor(GxEPD_WHITE); @@ -114,13 +127,16 @@ void updateRow3(String c) display.setFont(&orangeclock_icons15pt7b); display.setCursor(x, y); - display.print(ICON_SATS); + display.print(icon); display.setFont(&LibreFranklin_SemiBold15pt7b); display.setCursor(x + ROW3_ICONWIDTH, y); display.print(c); } while (display.nextPage()); + + currentRow3 = c; + currentIcon3 = icon; } void showSetupText(String t) @@ -133,7 +149,7 @@ void showSetupText(String t) // center the bounding box by transposition of the origin: uint16_t x = ((display.width() - (tbw)) / 2) - tbx; uint16_t y = ((display.height() - tbh) / 2) - tby; - + display.setFullWindow(); display.firstPage(); do { @@ -147,8 +163,11 @@ void showSetupText(String t) } while (display.nextPage()); } -void updateRow1(String c) +void updateRow1(String c, char icon) { + if (c.equals(currentRow1) && icon == currentIcon1) + return; + // struct tm timeinfo; // if (!getLocalTime(&timeinfo)) // { @@ -180,13 +199,16 @@ void updateRow1(String c) display.setFont(&ROW1_ICONFONT); display.setCursor(x, y); - display.print(ICON_PIE); + display.print(icon); display.setFont(&ROW1_FONT); display.setCursor(x + ROW1_ICONWIDTH, y); display.print(c); } while (display.nextPage()); + + currentRow1 = c; + currentIcon1 = icon; } void updateRows(String row1Content, String row2Content, String row3Content) diff --git a/src/epd.hpp b/src/epd.hpp index a19d202..ab02ff1 100644 --- a/src/epd.hpp +++ b/src/epd.hpp @@ -6,7 +6,7 @@ void setupDisplay(); void showSetupText(String t); -void updateRow1(String c); -void updateRow2(String c); -void updateRow3(String c); +void updateRow1(String c, char icon); +void updateRow2(String c, char icon); +void updateRow3(String c, char icon); void updateRows(String row1Content, String row2Content, String row3Content); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 049c592..2b42fb5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,7 +21,7 @@ GxEPD2_BW display = EPD_CLASS(4, 2, 3, 1); GxEPD2_BW display = EPD_CLASS(5, 3, 2, 1); #endif -typedef void (*MethodPtr)(String); +typedef void (*MethodPtr)(String, char); MethodPtr methods[] = {nullptr, updateRow1, updateRow2, updateRow3}; @@ -96,35 +96,40 @@ void loop() for (uint i = 1; i <= 3; i++) { String rowContent = ""; + char icon; char keyName[5]; snprintf(keyName, sizeof(keyName), "row%d", i); - Serial.print(keyName); - Serial.print(" "); - Serial.println(preferences.getUInt(keyName)); switch (preferences.getUInt(keyName)) { case LINE_BLOCKHEIGHT: + icon = ICON_BLOCK; rowContent = getBlock(); break; case LINE_MEMPOOL_FEES: + icon = ICON_PIE; rowContent = getMempoolFees(); break; case LINE_MEMPOOL_FEES_MEDIAN: + icon = ICON_PIE; rowContent = "NOT IMPL"; break; case LINE_HALVING_COUNTDOWN: + icon = ICON_HOURGLASS; rowContent = "NOT IMPL"; break; case LINE_SATSPERUNIT: { + icon = ICON_SATS; uint satsPerDollar = int(round(1 / float(getPrice()) * 10e7)); rowContent = satsPerDollar; break; } case LINE_FIATPRICE: + icon = ICON_DOLLAR; rowContent = getPrice(); break; case LINE_MARKETCAP: + icon = ICON_GLOBE; rowContent = "NOT IMPL"; break; case LINE_TIME: @@ -137,7 +142,7 @@ void loop() rowContent = "DEFAULT"; } - methods[i](rowContent); + methods[i](rowContent, icon); } // String block = String(getBlock()); diff --git a/src/shared.cpp b/src/shared.cpp new file mode 100644 index 0000000..3258a71 --- /dev/null +++ b/src/shared.cpp @@ -0,0 +1,13 @@ +#include "shared.hpp" + +String getMyHostname() +{ + uint8_t mac[6]; + // WiFi.macAddress(mac); + esp_efuse_mac_get_default(mac); + char hostname[15]; + String hostnamePrefix = preferences.getString(SETTING_HOSTNAME_PREFIX); + snprintf(hostname, sizeof(hostname), "%s-%02x%02x%02x", hostnamePrefix, + mac[3], mac[4], mac[5]); + return hostname; +} \ No newline at end of file diff --git a/src/shared.hpp b/src/shared.hpp index 52ef439..b4a5b81 100644 --- a/src/shared.hpp +++ b/src/shared.hpp @@ -37,29 +37,30 @@ #define SETUPFONT LibreFranklin_SemiBold12pt7b #endif -#define ICON_BLOCK "A" -#define ICON_EURO "B" -#define ICON_POUND "C" -#define ICON_YEN "D" -#define ICON_DOLLAR "E" -#define ICON_PIE "F" -#define ICON_GLOBE "G" -#define ICON_HOURGLASS "H" -#define ICON_LIGHTNING "I" -#define ICON_REFRESH "J" -#define ICON_NUCLEAR "K" -#define ICON_SATS "L" -#define ICON_SATUSD "M" -#define ICON_SETTINGS "N" -#define ICON_WIFI "O" -#define ICON_CROSS "P" -#define ICON_CHECK "Q" -#define ICON_WARNING "R" +#define ICON_BLOCK 'A' +#define ICON_EURO 'B' +#define ICON_POUND 'C' +#define ICON_YEN 'D' +#define ICON_DOLLAR 'E' +#define ICON_PIE 'F' +#define ICON_GLOBE 'G' +#define ICON_HOURGLASS 'H' +#define ICON_LIGHTNING 'I' +#define ICON_REFRESH 'J' +#define ICON_NUCLEAR 'K' +#define ICON_SATS 'L' +#define ICON_SATUSD 'M' +#define ICON_SETTINGS 'N' +#define ICON_WIFI 'O' +#define ICON_CROSS 'P' +#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" +#define SETTING_HOSTNAME_PREFIX "hostnamePrefix" const int LINE_BLOCKHEIGHT = 0; const int LINE_MEMPOOL_FEES = 1; @@ -82,4 +83,14 @@ const int LINE_DATE = 100; extern WiFiClientSecure client; extern GxEPD2_BW display; extern Preferences preferences; -extern bool isUpdating; \ No newline at end of file +extern bool isUpdating; + +extern String currentRow1; +extern String currentRow2; +extern String currentRow3; + +extern char currentIcon1; +extern char currentIcon2; +extern char currentIcon3; + +String getMyHostname(); diff --git a/src/webserver.cpp b/src/webserver.cpp index 785de57..18f6720 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -13,6 +13,7 @@ void setupWebserver() Serial.println(F("An Error has occurred while mounting LittleFS")); } + server.on("/api/status", HTTP_GET, onApiStatus); server.on("/api/settings", HTTP_GET, onApiSettingsGet); AsyncCallbackJsonWebHandler *settingsPatchHandler = new AsyncCallbackJsonWebHandler("/api/json/settings", onApiSettingsPatch); @@ -31,6 +32,31 @@ void setupWebserver() server.begin(); } +void onApiStatus(AsyncWebServerRequest *request) +{ + JsonDocument root; + + root["row1"] = currentRow1; + root["row2"] = currentRow2; + root["row3"] = currentRow3; + + root["icon1"] = String(currentIcon1); + root["icon2"] = String(currentIcon2); + root["icon3"] = String(currentIcon3); + + root["espUptime"] = esp_timer_get_time() / 1000000; + root["espFreeHeap"] = ESP.getFreeHeap(); + root["espHeapSize"] = ESP.getHeapSize(); + + root["rssi"] = WiFi.RSSI(); + + AsyncResponseStream *response = + request->beginResponseStream("application/json"); + serializeJson(root, *response); + + request->send(response); +} + void onApiSettingsGet(AsyncWebServerRequest *request) { JsonDocument root; @@ -49,6 +75,17 @@ void onApiSettingsGet(AsyncWebServerRequest *request) root[setting] = preferences.getBool(setting.c_str()); } + root["hostname"] = getMyHostname(); + root["ip"] = WiFi.localIP(); + root["txPower"] = WiFi.getTxPower(); + +#ifdef GIT_REV + root["gitRev"] = String(GIT_REV); +#endif +#ifdef LAST_BUILD_TIME + root["lastBuildTime"] = String(LAST_BUILD_TIME); +#endif + AsyncResponseStream *response = request->beginResponseStream("application/json"); serializeJson(root, *response); diff --git a/src/webserver.hpp b/src/webserver.hpp index 7ca57cc..61a154d 100644 --- a/src/webserver.hpp +++ b/src/webserver.hpp @@ -9,6 +9,8 @@ void setupWebserver(); +void onApiStatus(AsyncWebServerRequest *request); + void onApiSettingsGet(AsyncWebServerRequest *request); void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json); From 6c3796c776639cfb95fed917b1888587a55f2f12 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sun, 17 Mar 2024 03:18:16 +0100 Subject: [PATCH 06/22] More API calls, custom mempool instance, marketcap info --- src/config.cpp | 24 ++++++++++++ src/config.hpp | 1 + src/data.cpp | 93 +++++++++++++++++++++++++++++++++++++++++++---- src/data.hpp | 4 +- src/main.cpp | 28 +++++++++++--- src/shared.hpp | 5 ++- src/webserver.cpp | 23 +++++++++++- src/webserver.hpp | 2 + 8 files changed, 163 insertions(+), 17 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index 65ad555..c7eba0f 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -43,6 +43,11 @@ void setupPreferences() { preferences.putString(SETTING_HOSTNAME_PREFIX, "oc"); } + + if (!preferences.isKey(SETTING_MEMPOOL_INSTANCE)) + { + preferences.putString(SETTING_MEMPOOL_INSTANCE, "https://mempool.space"); + } } void setupWifi() @@ -227,3 +232,22 @@ void OTAUpdateTask(void *pvParameters) { } } +char getCurrencyIcon() { + char ret; + const char* currency = preferences.getString(SETTING_CURRENCY).c_str(); + if (strcmp(currency, CURRENCY_USD) == 0) { + ret = ICON_DOLLAR; + } else if(strcmp(currency, CURRENCY_EUR) == 0) { + ret = ICON_EURO; + } + // break; + // case CURRENCY_GBP: + // ret = ICON_POUND; + // break; + // case CURRENCY_JPY: + // ret = ICON_YEN; + // break; + // } + + return ret; +} \ No newline at end of file diff --git a/src/config.hpp b/src/config.hpp index 1312aa6..c4936e4 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -21,3 +21,4 @@ void wakeModemSleep(); void setModemSleep(); bool inPowerSaveMode(); +char getCurrencyIcon(); \ No newline at end of file diff --git a/src/data.cpp b/src/data.cpp index b5f7eca..eb92216 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -1,10 +1,10 @@ #include "data.hpp" -const String mempoolInstance = "https://mempool.space"; +//const String mempoolInstance = "https://mempool.space"; -const String mempoolPriceApiUrl = mempoolInstance + "/api/v1/prices"; -const String mempoolBlockApiUrl = mempoolInstance + "/api/blocks/tip/height"; -const String mempoolFeeApiUrl = mempoolInstance + "/api/v1/fees/recommended"; +const String mempoolPriceApi = "/api/v1/prices"; +const String mempoolBlockApi = "/api/blocks/tip/height"; +const String mempoolFeeApi = "/api/v1/fees/recommended"; uint lastPrice; uint lastBlock; @@ -14,7 +14,7 @@ uint getPrice() HTTPClient http; // Send HTTP request to CoinGecko API - http.begin(mempoolPriceApiUrl); + http.begin(preferences.getString(SETTING_MEMPOOL_INSTANCE) + mempoolPriceApi); int httpCode = http.GET(); @@ -44,7 +44,7 @@ uint getBlock() HTTPClient http; // Send HTTP request to CoinGecko API - http.begin(mempoolBlockApiUrl); + http.begin(preferences.getString(SETTING_MEMPOOL_INSTANCE) + mempoolBlockApi); int httpCode = http.GET(); @@ -71,7 +71,7 @@ String getMempoolFees() HTTPClient http; // Send HTTP request to CoinGecko API - http.begin(mempoolFeeApiUrl); + http.begin(preferences.getString(SETTING_MEMPOOL_INSTANCE) + mempoolFeeApi); int httpCode = http.GET(); @@ -95,4 +95,83 @@ String getMempoolFees() http.end(); return ""; +} + +double getSupplyAtBlock(std::uint32_t blockNr) +{ + if (blockNr >= 33 * 210000) + { + return 20999999.9769; + } + + const int initialBlockReward = 50; // Initial block reward + const int halvingInterval = 210000; // Number of blocks before halving + + int halvingCount = blockNr / halvingInterval; + double totalBitcoinInCirculation = 0; + + for (int i = 0; i < halvingCount; ++i) + { + totalBitcoinInCirculation += halvingInterval * initialBlockReward * std::pow(0.5, i); + } + + totalBitcoinInCirculation += (blockNr % halvingInterval) * initialBlockReward * std::pow(0.5, halvingCount); + + return totalBitcoinInCirculation; +} + +String formatNumberWithSuffix(std::uint64_t num, int numCharacters) +{ + static char result[20]; // Adjust size as needed + const long long quadrillion = 1000000000000000LL; + const long long trillion = 1000000000000LL; + const long long billion = 1000000000; + const long long million = 1000000; + const long long thousand = 1000; + + double numDouble = (double)num; + int numDigits = (int)log10(num) + 1; + char suffix; + + if (num >= quadrillion || numDigits > 15) + { + numDouble /= quadrillion; + suffix = 'Q'; + } + else if (num >= trillion || numDigits > 12) + { + numDouble /= trillion; + suffix = 'T'; + } + else if (num >= billion || numDigits > 9) + { + numDouble /= billion; + suffix = 'B'; + } + else if (num >= million || numDigits > 6) + { + numDouble /= million; + suffix = 'M'; + } + else if (num >= thousand || numDigits > 3) + { + numDouble /= thousand; + suffix = 'K'; + } + else + { + sprintf(result, "%llu", (unsigned long long)num); + return result; + } + + // Add suffix + int len = snprintf(result, sizeof(result), "%.0f%c", numDouble, suffix); + + // If there's room, add decimal places + if (len < numCharacters) + { + snprintf(result, sizeof(result), "%.*f%c", numCharacters - len - 1, numDouble, suffix); + } + + return result; } \ No newline at end of file diff --git a/src/data.hpp b/src/data.hpp index 9b2e363..176db2a 100644 --- a/src/data.hpp +++ b/src/data.hpp @@ -9,4 +9,6 @@ uint getPrice(); uint getBlock(); -String getMempoolFees(); \ No newline at end of file +String getMempoolFees(); +double getSupplyAtBlock(std::uint32_t blockNr); +String formatNumberWithSuffix(std::uint64_t num, int numCharacters); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 2b42fb5..3b111b2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -114,9 +114,12 @@ void loop() rowContent = "NOT IMPL"; break; case LINE_HALVING_COUNTDOWN: + { icon = ICON_HOURGLASS; - rowContent = "NOT IMPL"; + uint currentBlock = getBlock(); + rowContent = 210000 - (currentBlock % 210000); break; + } case LINE_SATSPERUNIT: { icon = ICON_SATS; @@ -125,19 +128,32 @@ void loop() break; } case LINE_FIATPRICE: - icon = ICON_DOLLAR; + icon = getCurrencyIcon(); rowContent = getPrice(); break; case LINE_MARKETCAP: - icon = ICON_GLOBE; - rowContent = "NOT IMPL"; + { + icon = getCurrencyIcon(); + int64_t marketCap = static_cast(getSupplyAtBlock(getBlock()) * double(getPrice())); + rowContent = String(formatNumberWithSuffix(marketCap, 4)); break; + } case LINE_TIME: - rowContent = "NOT IMPL"; + { + icon = ICON_GLOBE; + char dateString[10]; + strftime(dateString, 10, "%H:%M:%S", &timeinfo); + rowContent = dateString; break; + } case LINE_DATE: - rowContent = "NOT IMPL"; + { + icon = ICON_GLOBE; + char dateString[10]; + strftime(dateString, 10, "%x", &timeinfo); + rowContent = dateString; break; + } default: rowContent = "DEFAULT"; } diff --git a/src/shared.hpp b/src/shared.hpp index b4a5b81..fff9a85 100644 --- a/src/shared.hpp +++ b/src/shared.hpp @@ -26,13 +26,13 @@ #define EPD_CLASS GxEPD2_290_T94 #define ROW1_FONT LibreFranklin_SemiBold15pt7b #define ROW1_ICONFONT orangeclock_icons14pt7b - #define ROW1_ICONWIDTH 27 + #define ROW1_ICONWIDTH 29 #define ROW2_FONT LibreFranklin_Bold25pt7b #define ROW2_ICONFONT orangeclock_icons25pt7b #define ROW2_ICONWIDTH 52 #define ROW3_FONT LibreFranklin_SemiBold15pt7b #define ROW3_ICONFONT orangeclock_icons14pt7b - #define ROW3_ICONWIDTH 27 + #define ROW3_ICONWIDTH 29 #define SETUPFONT LibreFranklin_SemiBold12pt7b #endif @@ -61,6 +61,7 @@ #define SETTING_ROW3_CONTENT "row3" #define SETTING_CURRENCY "currency" #define SETTING_HOSTNAME_PREFIX "hostnamePrefix" +#define SETTING_MEMPOOL_INSTANCE "mempoolInstance" const int LINE_BLOCKHEIGHT = 0; const int LINE_MEMPOOL_FEES = 1; diff --git a/src/webserver.cpp b/src/webserver.cpp index 18f6720..159b85b 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -3,7 +3,7 @@ AsyncWebServer server(80); const String uintSettings[] = {SETTING_ROW1_CONTENT, SETTING_ROW2_CONTENT, SETTING_ROW3_CONTENT}; -const String stringSettings[] = {SETTING_CURRENCY}; +const String stringSettings[] = {SETTING_CURRENCY,SETTING_MEMPOOL_INSTANCE}; const String boolSettings[] = {}; void setupWebserver() @@ -15,6 +15,9 @@ void setupWebserver() server.on("/api/status", HTTP_GET, onApiStatus); server.on("/api/settings", HTTP_GET, onApiSettingsGet); + server.on("/api/restart", HTTP_GET, onApiRestart); + server.on("/api/full_refresh", HTTP_GET, onApiFullRefresh); + AsyncCallbackJsonWebHandler *settingsPatchHandler = new AsyncCallbackJsonWebHandler("/api/json/settings", onApiSettingsPatch); server.addHandler(settingsPatchHandler); @@ -150,3 +153,21 @@ void onNotFound(AsyncWebServerRequest *request) request->send(404); } } + +void onApiRestart(AsyncWebServerRequest *request) { + request->send(200); + + delay(500); + + esp_restart(); +} + +/** + * @Api + * @Path("/api/full_refresh") + */ +void onApiFullRefresh(AsyncWebServerRequest *request) { + display.refresh(); + + request->send(200); +} \ No newline at end of file diff --git a/src/webserver.hpp b/src/webserver.hpp index 61a154d..30230de 100644 --- a/src/webserver.hpp +++ b/src/webserver.hpp @@ -13,6 +13,8 @@ void onApiStatus(AsyncWebServerRequest *request); void onApiSettingsGet(AsyncWebServerRequest *request); void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json); +void onApiFullRefresh(AsyncWebServerRequest *request); +void onApiRestart(AsyncWebServerRequest *request); void onIndex(AsyncWebServerRequest *request); void onNotFound(AsyncWebServerRequest *request); \ No newline at end of file From 585b50d0bac0600540c0eaeae10d33b564ec2be7 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sun, 17 Mar 2024 18:47:26 +0100 Subject: [PATCH 07/22] Add missing settings, webUI as submodule --- .gitmodules | 3 ++ data | 1 + data/build/index.html | 0 platformio.ini | 3 +- scripts/extra_script.py | 38 +++++++++++++++++++ src/config.cpp | 84 +++++++++++++++++++++++++++++------------ src/main.cpp | 11 +++--- src/shared.cpp | 11 ------ src/shared.hpp | 8 +++- src/utils.cpp | 32 ++++++++++++++++ src/utils.hpp | 27 +++++++++++++ src/webserver.cpp | 36 +++++++++++++++++- 12 files changed, 209 insertions(+), 45 deletions(-) create mode 100644 .gitmodules create mode 160000 data delete mode 100644 data/build/index.html create mode 100644 scripts/extra_script.py create mode 100644 src/utils.cpp create mode 100644 src/utils.hpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f9d84d8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "data"] + path = data + url = https://github.com/btclock/oc-webui.git diff --git a/data b/data new file mode 160000 index 0000000..9df3329 --- /dev/null +++ b/data @@ -0,0 +1 @@ +Subproject commit 9df3329847f3939dc1242136e19746613fb83c4b diff --git a/data/build/index.html b/data/build/index.html deleted file mode 100644 index e69de29..0000000 diff --git a/platformio.ini b/platformio.ini index 643b015..d86dcf0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,7 +9,7 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -data_dir = data/build +data_dir = data/build_gz default_envs = lolin_s2_mini_213epd, lolin_s2_mini_29epd, lolin_s3_mini_213epd, lolin_s3_mini_29epd [btclock_base] @@ -19,6 +19,7 @@ monitor_speed = 115200 monitor_filters = esp32_exception_decoder, colorize board_build.filesystem = littlefs board_build.partitions = partition.csv +extra_scripts = post:scripts/extra_script.py build_flags = !python scripts/git_rev.py -DLAST_BUILD_TIME=$UNIX_TIME diff --git a/scripts/extra_script.py b/scripts/extra_script.py new file mode 100644 index 0000000..4a1c118 --- /dev/null +++ b/scripts/extra_script.py @@ -0,0 +1,38 @@ +Import("env") +import os +import gzip +from shutil import copyfileobj, rmtree +from pathlib import Path + +def gzip_file(input_file, output_file): + with open(input_file, 'rb') as f_in: + with gzip.open(output_file, 'wb') as f_out: + copyfileobj(f_in, f_out) + +def process_directory(input_dir, output_dir): + if os.path.exists(output_dir): + rmtree(output_dir) + for root, dirs, files in os.walk(input_dir): + relative_path = os.path.relpath(root, input_dir) + output_root = os.path.join(output_dir, relative_path) + + Path(output_root).mkdir(parents=True, exist_ok=True) + + for file in files: + # if file.endswith(('.html', '.css', '.js')): + input_file_path = os.path.join(root, file) + output_file_path = os.path.join(output_root, file + '.gz') + gzip_file(input_file_path, output_file_path) + print(f'Compressed: {input_file_path} -> {output_file_path}') + + + +# Build web interface before building FS +def before_buildfs(source, target, env): + env.Execute("cd data && yarn && yarn postinstall && yarn build") + input_directory = 'data/dist' + output_directory = 'data/build_gz' + process_directory(input_directory, output_directory) + +os.environ["PUBLIC_BASE_URL"] = "" +env.AddPreAction("$BUILD_DIR/littlefs.bin", before_buildfs) diff --git a/src/config.cpp b/src/config.cpp index c7eba0f..fd3e90b 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -3,7 +3,7 @@ Preferences preferences; const char *ntpServer = "pool.ntp.org"; -const long gmtOffset_sec = 0; +// const long gmtOffset_sec = 0; const int daylightOffset_sec = 3600; TaskHandle_t OTAHandle = NULL; @@ -14,7 +14,7 @@ bool isUpdating = false; void setupTime() { - configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); + configTime(preferences.getInt(SETTING_TIME_OFFSET_MIN), daylightOffset_sec, ntpServer); } void setupPreferences() @@ -48,10 +48,44 @@ void setupPreferences() { preferences.putString(SETTING_MEMPOOL_INSTANCE, "https://mempool.space"); } + + if (!preferences.isKey(SETTING_TIME_FORMAT)) + { + preferences.putString(SETTING_TIME_FORMAT, "%H:%M:%S"); + } + + if (!preferences.isKey(SETTING_DATE_FORMAT)) + { + preferences.putString(SETTING_DATE_FORMAT, "%d-%m-%Y"); + } + + if (!preferences.isKey(SETTING_DECIMAL_SEPARATOR)) + { + preferences.putChar(SETTING_DECIMAL_SEPARATOR, '.'); + } + + if (!preferences.isKey(SETTING_POWER_SAVE_MODE)) + { + preferences.putBool(SETTING_POWER_SAVE_MODE, false); + } + + if (!preferences.isKey(SETTING_TIME_OFFSET_MIN)) + { + preferences.putInt(SETTING_TIME_OFFSET_MIN, 0); + } } void setupWifi() { + uint8_t mac[6]; + WiFi.macAddress(mac); + unsigned long seed = 0; + for (int i = 0; i < 6; i++) + { + seed += (unsigned long)mac[i] << ((i & 1) * 8); + } + randomSeed(seed); + // WiFi.begin(, "); WiFi.setAutoConnect(true); WiFi.setAutoReconnect(true); @@ -75,15 +109,10 @@ void setupWifi() } } - byte mac[6]; - WiFi.macAddress(mac); String softAP_SSID = String("OrangeBTClock"); WiFi.setHostname(softAP_SSID.c_str()); - String softAP_password = - base64::encode(String(mac[2], 16) + String(mac[4], 16) + - String(mac[5], 16) + String(mac[1], 16)) - .substring(2, 10); + String softAP_password = getAPPassword(); // wm.setConfigPortalTimeout(preferences.getUInt("wpTimeout", 600)); wm.setWiFiAutoReconnect(false); @@ -214,31 +243,36 @@ void setupOTA() ArduinoOTA.begin(); xTaskCreatePinnedToCore( - OTAUpdateTask, // Task function - "OTAUpdateTask", // Task name - 4096, // Stack size - NULL, // Task parameters - 1, // Priority (higher value means higher priority) - &OTAHandle, // Task handle - 0 // Core to run the task (0 or 1) + OTAUpdateTask, // Task function + "OTAUpdateTask", // Task name + 4096, // Stack size + NULL, // Task parameters + 1, // Priority (higher value means higher priority) + &OTAHandle, // Task handle + 0 // Core to run the task (0 or 1) ); } - -void OTAUpdateTask(void *pvParameters) { - for (;;) { - ArduinoOTA.handle(); // Handle OTA updates +void OTAUpdateTask(void *pvParameters) +{ + for (;;) + { + ArduinoOTA.handle(); // Handle OTA updates vTaskDelay(1000 / portTICK_PERIOD_MS); // Delay to avoid high CPU usage } } -char getCurrencyIcon() { +char getCurrencyIcon() +{ char ret; - const char* currency = preferences.getString(SETTING_CURRENCY).c_str(); - if (strcmp(currency, CURRENCY_USD) == 0) { - ret = ICON_DOLLAR; - } else if(strcmp(currency, CURRENCY_EUR) == 0) { - ret = ICON_EURO; + const char *currency = preferences.getString(SETTING_CURRENCY).c_str(); + if (strcmp(currency, CURRENCY_USD) == 0) + { + ret = ICON_DOLLAR; + } + else if (strcmp(currency, CURRENCY_EUR) == 0) + { + ret = ICON_EURO; } // break; // case CURRENCY_GBP: diff --git a/src/main.cpp b/src/main.cpp index 3b111b2..f136a51 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -59,6 +59,7 @@ void loop() { if (isUpdating) { + delay(1000); return; } @@ -135,22 +136,22 @@ void loop() { icon = getCurrencyIcon(); int64_t marketCap = static_cast(getSupplyAtBlock(getBlock()) * double(getPrice())); - rowContent = String(formatNumberWithSuffix(marketCap, 4)); + rowContent = String(formatNumberWithSuffix(marketCap, 8)); break; } case LINE_TIME: { icon = ICON_GLOBE; - char dateString[10]; - strftime(dateString, 10, "%H:%M:%S", &timeinfo); + char dateString[16]; + strftime(dateString, sizeof(dateString), preferences.getString(SETTING_TIME_FORMAT).c_str(), &timeinfo); rowContent = dateString; break; } case LINE_DATE: { icon = ICON_GLOBE; - char dateString[10]; - strftime(dateString, 10, "%x", &timeinfo); + char dateString[16]; + strftime(dateString, sizeof(dateString), preferences.getString(SETTING_DATE_FORMAT).c_str(), &timeinfo); rowContent = dateString; break; } diff --git a/src/shared.cpp b/src/shared.cpp index 3258a71..2547a42 100644 --- a/src/shared.cpp +++ b/src/shared.cpp @@ -1,13 +1,2 @@ #include "shared.hpp" -String getMyHostname() -{ - uint8_t mac[6]; - // WiFi.macAddress(mac); - esp_efuse_mac_get_default(mac); - char hostname[15]; - String hostnamePrefix = preferences.getString(SETTING_HOSTNAME_PREFIX); - snprintf(hostname, sizeof(hostname), "%s-%02x%02x%02x", hostnamePrefix, - mac[3], mac[4], mac[5]); - return hostname; -} \ No newline at end of file diff --git a/src/shared.hpp b/src/shared.hpp index fff9a85..ec281c6 100644 --- a/src/shared.hpp +++ b/src/shared.hpp @@ -5,6 +5,7 @@ #include #include #include +#include "utils.hpp" #include "fonts/fonts.hpp" #ifdef VERSION_EPD_2_13 @@ -62,6 +63,12 @@ #define SETTING_CURRENCY "currency" #define SETTING_HOSTNAME_PREFIX "hostnamePrefix" #define SETTING_MEMPOOL_INSTANCE "mempoolInstance" +#define SETTING_POWER_SAVE_MODE "powerSaveMode" +#define SETTING_TIME_OFFSET_MIN "timeOffsetMin" +#define SETTING_DECIMAL_SEPARATOR "decSeparator" +#define SETTING_TIME_FORMAT "timeFormat" +#define SETTING_DATE_FORMAT "dateFormat" + const int LINE_BLOCKHEIGHT = 0; const int LINE_MEMPOOL_FEES = 1; @@ -94,4 +101,3 @@ extern char currentIcon1; extern char currentIcon2; extern char currentIcon3; -String getMyHostname(); diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..38731ea --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,32 @@ +#include "utils.hpp" + +String getAPPassword() +{ + byte mac[6]; + WiFi.macAddress(mac); + const char charset[] = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789"; + char password[9]; // 8 characters + null terminator + snprintf(password, sizeof(password), "%c%c%c%c%c%c%c%c", + charset[mac[0] % (sizeof(charset) - 1)], + charset[mac[1] % (sizeof(charset) - 1)], + charset[mac[2] % (sizeof(charset) - 1)], + charset[mac[3] % (sizeof(charset) - 1)], + charset[mac[4] % (sizeof(charset) - 1)], + charset[mac[5] % (sizeof(charset) - 1)], + charset[(mac[0] + mac[1] + mac[2] + mac[3] + mac[4] + mac[5]) % (sizeof(charset) - 1)], + charset[(mac[0] * mac[1] * mac[2] * mac[3] * mac[4] * mac[5]) % (sizeof(charset) - 1)]); + + return password; +} + +String getMyHostname() +{ + uint8_t mac[6]; + // WiFi.macAddress(mac); + esp_efuse_mac_get_default(mac); + char hostname[15]; + String hostnamePrefix = preferences.getString(SETTING_HOSTNAME_PREFIX); + snprintf(hostname, sizeof(hostname), "%s-%02x%02x%02x", hostnamePrefix, + mac[3], mac[4], mac[5]); + return hostname; +} \ No newline at end of file diff --git a/src/utils.hpp b/src/utils.hpp new file mode 100644 index 0000000..8c32d47 --- /dev/null +++ b/src/utils.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include "shared.hpp" + +namespace ArduinoJson { +template <> +struct Converter { + static void toJson(char c, JsonVariant var) { + var.set(static_cast(c)); + } + + static char fromJson(JsonVariantConst src) { + return static_cast(src.as()); + } + + static bool checkJson(JsonVariantConst src) { + return src.is(); + } +}; +} + + +String getAPPassword(); +String getMyHostname(); diff --git a/src/webserver.cpp b/src/webserver.cpp index 159b85b..758d4b7 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -3,8 +3,10 @@ AsyncWebServer server(80); const String uintSettings[] = {SETTING_ROW1_CONTENT, SETTING_ROW2_CONTENT, SETTING_ROW3_CONTENT}; -const String stringSettings[] = {SETTING_CURRENCY,SETTING_MEMPOOL_INSTANCE}; -const String boolSettings[] = {}; +const String intSettings[] = {SETTING_TIME_OFFSET_MIN}; +const String stringSettings[] = {SETTING_CURRENCY, SETTING_MEMPOOL_INSTANCE, SETTING_TIME_FORMAT, SETTING_DATE_FORMAT}; +const String charSettings[] = {SETTING_DECIMAL_SEPARATOR}; +const String boolSettings[] = {SETTING_POWER_SAVE_MODE}; void setupWebserver() { @@ -68,11 +70,21 @@ void onApiSettingsGet(AsyncWebServerRequest *request) root[setting] = preferences.getUInt(setting.c_str()); } + for (String setting : intSettings) + { + root[setting] = preferences.getInt(setting.c_str()); + } + for (String setting : stringSettings) { root[setting] = preferences.getString(setting.c_str()); } + for (String setting : charSettings) + { + root[setting] = preferences.getChar(setting.c_str()); + } + for (String setting : boolSettings) { root[setting] = preferences.getBool(setting.c_str()); @@ -110,6 +122,16 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json) } } + for (String setting : intSettings) + { + if (settings.containsKey(setting)) + { + preferences.putInt(setting.c_str(), settings[setting].as()); + Serial.printf("Setting %s to %d\r\n", setting.c_str(), + settings[setting].as()); + } + } + for (String setting : stringSettings) { if (settings.containsKey(setting)) @@ -120,6 +142,16 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json) } } + for (String setting : charSettings) + { + if (settings.containsKey(setting)) + { + preferences.putChar(setting.c_str(), settings[setting].as()); + Serial.printf("Setting %s to %s\r\n", setting.c_str(), + settings[setting].as()); + } + } + for (String setting : boolSettings) { if (settings.containsKey(setting)) From a2b6f234f128b72518f0c86559f119ba4142ed09 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sun, 17 Mar 2024 19:49:41 +0100 Subject: [PATCH 08/22] Fix WebUI, implement median mempool fee screen --- data | 2 +- src/config.cpp | 22 +++++++++++----------- src/data.cpp | 32 ++++++++++++++++++++++++++++++++ src/data.hpp | 1 + src/main.cpp | 2 +- src/webserver.cpp | 1 + 6 files changed, 47 insertions(+), 13 deletions(-) diff --git a/data b/data index 9df3329..bd12ce4 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 9df3329847f3939dc1242136e19746613fb83c4b +Subproject commit bd12ce4362f0b665a33e1f637136870a667dbdfc diff --git a/src/config.cpp b/src/config.cpp index fd3e90b..5144416 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -265,23 +265,23 @@ void OTAUpdateTask(void *pvParameters) char getCurrencyIcon() { char ret; - const char *currency = preferences.getString(SETTING_CURRENCY).c_str(); - if (strcmp(currency, CURRENCY_USD) == 0) + String currency = preferences.getString(SETTING_CURRENCY); + if (currency.equals(CURRENCY_USD)) { ret = ICON_DOLLAR; } - else if (strcmp(currency, CURRENCY_EUR) == 0) + else if (currency.equals(CURRENCY_EUR)) { ret = ICON_EURO; } - // break; - // case CURRENCY_GBP: - // ret = ICON_POUND; - // break; - // case CURRENCY_JPY: - // ret = ICON_YEN; - // break; - // } + else if (currency.equals(CURRENCY_GBP)) + { + ret = ICON_POUND; + } + else if (currency.equals(CURRENCY_JPY)) + { + ret = ICON_YEN; + } return ret; } \ No newline at end of file diff --git a/src/data.cpp b/src/data.cpp index eb92216..eab3341 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -5,6 +5,7 @@ const String mempoolPriceApi = "/api/v1/prices"; const String mempoolBlockApi = "/api/blocks/tip/height"; const String mempoolFeeApi = "/api/v1/fees/recommended"; +const String mempoolMedianFeeApi = "/api/v1/fees/mempool-blocks"; uint lastPrice; uint lastBlock; @@ -97,6 +98,37 @@ String getMempoolFees() return ""; } +uint getMempoolFeesMedian() +{ + HTTPClient http; + + // Send HTTP request to CoinGecko API + http.begin(preferences.getString(SETTING_MEMPOOL_INSTANCE) + mempoolMedianFeeApi); + + int httpCode = http.GET(); + + if (httpCode == 200) + { + char feeString[20]; + String payload = http.getString(); + JsonDocument doc; + deserializeJson(doc, payload); + + snprintf(feeString, 20, "L: %d M: %d H: %d", doc["hourFee"].as(), doc["halfHourFee"].as(), doc["fastestFee"].as()); + + return round(doc[0]["medianFee"].as()); + + // preferences.putUInt("lastPrice", eurPrice); + } + else + { + Serial.printf("HTTP GET request mempool median fees failed with error: %s\n", http.errorToString(httpCode).c_str()); + } + http.end(); + + return 0; +} + double getSupplyAtBlock(std::uint32_t blockNr) { if (blockNr >= 33 * 210000) diff --git a/src/data.hpp b/src/data.hpp index 176db2a..3c7e94e 100644 --- a/src/data.hpp +++ b/src/data.hpp @@ -10,5 +10,6 @@ uint getPrice(); uint getBlock(); String getMempoolFees(); +uint getMempoolFeesMedian(); double getSupplyAtBlock(std::uint32_t blockNr); String formatNumberWithSuffix(std::uint64_t num, int numCharacters); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index f136a51..bb44a0a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -112,7 +112,7 @@ void loop() break; case LINE_MEMPOOL_FEES_MEDIAN: icon = ICON_PIE; - rowContent = "NOT IMPL"; + rowContent = getMempoolFeesMedian(); break; case LINE_HALVING_COUNTDOWN: { diff --git a/src/webserver.cpp b/src/webserver.cpp index 758d4b7..69b894c 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -25,6 +25,7 @@ void setupWebserver() server.addHandler(settingsPatchHandler); server.serveStatic("/build", LittleFS, "/build"); + server.serveStatic("/fonts", LittleFS, "/fonts"); server.on("/", HTTP_GET, onIndex); server.onNotFound(onNotFound); From 4b0a31a2ed5bb7051f38cf6d7a5d9d902a774887 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sun, 17 Mar 2024 22:27:10 +0100 Subject: [PATCH 09/22] Add support for OrangeClock PCB, add README --- .github/workflows/tagging.yml | 5 +++ README.md | 13 ++++++++ boards/orangeclock.json | 62 +++++++++++++++++++++++++++++++++++ platformio.ini | 15 +++++++-- src/config.cpp | 2 ++ src/epd.cpp | 2 +- src/main.cpp | 29 ++++++++++++++++ 7 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 README.md create mode 100644 boards/orangeclock.json diff --git a/.github/workflows/tagging.yml b/.github/workflows/tagging.yml index 29b4b62..07e8ba4 100644 --- a/.github/workflows/tagging.yml +++ b/.github/workflows/tagging.yml @@ -37,6 +37,11 @@ jobs: version: esp32s2 - name: lolin_s3_mini version: esp32s3 + - name: orangeclock + version: esp32s3 + exclude: + - chip: orangeclock + epd_variant: 213epd runs-on: ubuntu-latest permissions: contents: write diff --git a/README.md b/README.md new file mode 100644 index 0000000..1fb871f --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# OrangeBTClock (working title) + +[![BTClock CI](https://github.com/btclock/OrangeBTClock/actions/workflows/tagging.yml/badge.svg)](https://github.com/btclock/OrangeBTClock/actions/workflows/tagging.yml) + +Firmware for cheap ESP32-S2/S3 hardware combined with a eInk display + +See releases for prebuilt binaries, ready to flash (e.g. with the [esphome web flasher](https://web.esphome.io/)) + +## Development + +- [PlatformIO](https://platformio.org/platformio-ide). +- [Node.js](https://nodejs.org/en) and [yarn](https://yarnpkg.com/). + diff --git a/boards/orangeclock.json b/boards/orangeclock.json new file mode 100644 index 0000000..c852a39 --- /dev/null +++ b/boards/orangeclock.json @@ -0,0 +1,62 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32s3_out.ld", + "partitions": "default_8MB.csv", + "memory_type": "qio_opi" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_ORANGECLOCK", + "-DARDUINO_ESP32S3_DEV", + "-DIS_ORANGECLOCK", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1", + "-DARDUINO_USB_CDC_ON_BOOT=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "psram_type": "opi", + "espidf": { + "sdkconfig_path": "boards" + }, + "hwids": [ + [ + "0x303A", + "0x1001" + ] + ], + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": [ + "bluetooth", + "wifi" + ], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": [ + "esp-builtin" + ], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "OrangeClock", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 460800 + }, + "url": "http://github.com/btclock", + "vendor": "BTClock" +} \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index d86dcf0..613f146 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,7 +10,7 @@ [platformio] data_dir = data/build_gz -default_envs = lolin_s2_mini_213epd, lolin_s2_mini_29epd, lolin_s3_mini_213epd, lolin_s3_mini_29epd +default_envs = lolin_s2_mini_213epd, lolin_s2_mini_29epd, lolin_s3_mini_213epd, lolin_s3_mini_29epd, orangeclock_29epd [btclock_base] platform = espressif32 @@ -29,7 +29,7 @@ lib_deps = bblanchon/ArduinoJson@^7.0.3 mathieucarbou/ESP Async WebServer gilmaimon/ArduinoWebsockets@^0.5.3 - + fastled/FastLED@^3.6.0 [env:lolin_s2_mini] extends = btclock_base board = lolin_s2_mini @@ -60,4 +60,13 @@ build_flags = extends = env:lolin_s3_mini build_flags = ${btclock_base.build_flags} - -D VERSION_EPD_2_9 \ No newline at end of file + -D VERSION_EPD_2_9 + + +[env:orangeclock_29epd] +extends = btclock_base +board = orangeclock +build_flags = + ${btclock_base.build_flags} + -D VERSION_EPD_2_9 + -D IS_ORANGECLOCK \ No newline at end of file diff --git a/src/config.cpp b/src/config.cpp index 5144416..d676389 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -92,6 +92,7 @@ void setupWifi() WiFiManager wm; + #ifndef ARDUINO_ORANGECLOCK // Touch pin 14 to reset if (touchRead(14) > 9000) { @@ -108,6 +109,7 @@ void setupWifi() wm.resetSettings(); } } + #endif String softAP_SSID = String("OrangeBTClock"); diff --git a/src/epd.cpp b/src/epd.cpp index 10ad8d0..04b4e9f 100644 --- a/src/epd.cpp +++ b/src/epd.cpp @@ -51,7 +51,7 @@ void setupDisplay() // display.fillRect(0, row1, display.width(), 54, GxEPD_BLACK); display.displayWindow(0, row1, display.width(), row2); - display.display(true); + display.display(false); // display.fillRect(0, row2, display.width(), 54, GxEPD_BLACK); // display.displayWindow(0, row2, display.width(), 54); diff --git a/src/main.cpp b/src/main.cpp index bb44a0a..3537df6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ #include "config.hpp" #include "webserver.hpp" #include +#include #define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */ #define TIME_TO_SLEEP 5 /* Time ESP32 will go to sleep (in seconds) */ @@ -21,6 +22,10 @@ GxEPD2_BW display = EPD_CLASS(4, 2, 3, 1); GxEPD2_BW display = EPD_CLASS(5, 3, 2, 1); #endif +#ifdef ARDUINO_ORANGECLOCK +GxEPD2_BW display = EPD_CLASS(5, 3, 1, 2); +#endif + typedef void (*MethodPtr)(String, char); MethodPtr methods[] = {nullptr, updateRow1, updateRow2, updateRow3}; @@ -30,14 +35,25 @@ uint currentPrice = 0; String currentBlock = ""; String currentFees = ""; +#define NUM_LEDS 2 +CRGB leds[NUM_LEDS]; + void setup() { // setCpuFrequencyMhz(40); Serial.begin(115200); + #ifndef IS_ORANGECLOCK pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); + #else + FastLED.addLeds(leds, NUM_LEDS); + leds[0] = CRGB::DarkOrange; + leds[1] = CRGB::OrangeRed; + + FastLED.show(); + #endif setupPreferences(); setupDisplay(); @@ -52,7 +68,20 @@ void setup() } client.setInsecure(); + #ifndef IS_ORANGECLOCK digitalWrite(LED_BUILTIN, LOW); + #else + leds[0] = CRGB::Black; + leds[1] = CRGB::Black; + + FastLED.show(); + delay(100); + + display.setFullWindow(); + display.clearScreen(GxEPD_WHITE); + display.display(true); + + #endif } void loop() From 55defdfeaa3a51b9bc1a84b75be656eeb0a325e4 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sun, 17 Mar 2024 23:30:03 +0100 Subject: [PATCH 10/22] Fix matrix order in workflow --- .github/workflows/tagging.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tagging.yml b/.github/workflows/tagging.yml index 07e8ba4..5643d3b 100644 --- a/.github/workflows/tagging.yml +++ b/.github/workflows/tagging.yml @@ -31,7 +31,6 @@ jobs: continue-on-error: true strategy: matrix: - epd_variant: [213epd, 29epd] chip: - name: lolin_s2_mini version: esp32s2 @@ -39,6 +38,7 @@ jobs: version: esp32s3 - name: orangeclock version: esp32s3 + epd_variant: [213epd, 29epd] exclude: - chip: orangeclock epd_variant: 213epd From 5dda0da73eeab8b46f1d4715b57b34a4f1e6a7de Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sun, 17 Mar 2024 23:30:46 +0100 Subject: [PATCH 11/22] Enable setup-node step again --- .github/actions/install-build/action.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/actions/install-build/action.yml b/.github/actions/install-build/action.yml index 3850177..891a8a5 100644 --- a/.github/actions/install-build/action.yml +++ b/.github/actions/install-build/action.yml @@ -4,11 +4,11 @@ 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/setup-node@v4 + with: + node-version: lts/* + cache: yarn + cache-dependency-path: '**/yarn.lock' - uses: actions/cache@v3 with: path: | From 4c10ee9c1f6f8dc05d80ae4ff327386a3d5385cf Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Tue, 19 Mar 2024 01:35:08 +0100 Subject: [PATCH 12/22] Add OrangeClock logo to splash screen --- data | 2 +- src/bitmap.hpp | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/epd.cpp | 16 ++++++---- src/epd.hpp | 2 +- src/main.cpp | 12 ++++---- 5 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 src/bitmap.hpp diff --git a/data b/data index bd12ce4..2e9164f 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit bd12ce4362f0b665a33e1f637136870a667dbdfc +Subproject commit 2e9164fc8f2756ef55520978cf70214bde17364d diff --git a/src/bitmap.hpp b/src/bitmap.hpp new file mode 100644 index 0000000..72dbda9 --- /dev/null +++ b/src/bitmap.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include +// 'oclogo', 250x37px +const unsigned char epd_bitmap_oclogo [] PROGMEM = { + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0xc3, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xf0, 0x0c, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xe3, 0x9c, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0xc7, 0x9c, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, + 0x0f, 0x8f, 0x9c, 0x01, 0xe0, 0x00, 0x07, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, + 0x0f, 0x07, 0x9c, 0x00, 0xf0, 0x00, 0x1f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xc0, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, + 0x1e, 0x63, 0x9c, 0x00, 0x78, 0x00, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xf0, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, + 0x1e, 0x71, 0x9c, 0x00, 0x78, 0x00, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, + 0x3c, 0xf8, 0x9c, 0x00, 0x38, 0x01, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, + 0x3c, 0xfc, 0x1c, 0x00, 0x3c, 0x01, 0xfc, 0x1f, 0xe1, 0xf3, 0xc3, 0xfc, 0x03, 0xe7, 0xe0, 0x03, + 0xf3, 0xe0, 0x1f, 0xc0, 0x3f, 0xc1, 0xf0, 0xf8, 0x07, 0xf0, 0x00, 0x7f, 0x03, 0xf0, 0x7e, 0x00, + 0x39, 0xfe, 0x1c, 0x00, 0x1c, 0x03, 0xf8, 0x07, 0xe1, 0xff, 0xdf, 0xff, 0x03, 0xff, 0xf0, 0x0f, + 0xff, 0xe0, 0x7f, 0xf0, 0x3f, 0x80, 0x40, 0xf8, 0x1f, 0xfc, 0x01, 0xff, 0xc3, 0xf0, 0xfe, 0x00, + 0x39, 0xff, 0x1c, 0x00, 0x1c, 0x03, 0xf0, 0x07, 0xf1, 0xff, 0xdf, 0xff, 0x83, 0xff, 0xf8, 0x1f, + 0xff, 0xe0, 0xff, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0xff, 0x03, 0xff, 0xe3, 0xf1, 0xfc, 0x00, + 0x39, 0xff, 0x9c, 0x00, 0x1c, 0x03, 0xf0, 0x03, 0xf1, 0xff, 0xcf, 0xff, 0x83, 0xff, 0xfc, 0x3f, + 0xff, 0xe1, 0xff, 0xfc, 0x7e, 0x00, 0x00, 0xf8, 0x7f, 0xff, 0x07, 0xff, 0xf3, 0xf3, 0xf8, 0x00, + 0x78, 0x00, 0x1c, 0x00, 0x1c, 0x07, 0xe0, 0x03, 0xf1, 0xff, 0xce, 0x1f, 0xc3, 0xff, 0xfc, 0x3f, + 0x8f, 0xe1, 0xf8, 0x7e, 0x7e, 0x00, 0x00, 0xf8, 0x7f, 0x3f, 0x8f, 0xe3, 0xe3, 0xf7, 0xf0, 0x00, + 0x78, 0x00, 0x1e, 0x00, 0x1c, 0x07, 0xe0, 0x03, 0xf1, 0xfc, 0x00, 0x0f, 0xc3, 0xf0, 0xfc, 0x7f, + 0x07, 0xe3, 0xf0, 0x3e, 0x7e, 0x00, 0x00, 0xf8, 0xfc, 0x1f, 0x8f, 0xc1, 0xc3, 0xff, 0xe0, 0x00, + 0x78, 0x00, 0x1f, 0x00, 0x1c, 0x07, 0xe0, 0x03, 0xf1, 0xf8, 0x00, 0x0f, 0xc3, 0xe0, 0x7c, 0x7e, + 0x03, 0xe3, 0xf0, 0x3e, 0x7e, 0x00, 0x00, 0xf8, 0xfc, 0x0f, 0xcf, 0x80, 0x03, 0xff, 0xc0, 0x00, + 0x39, 0xff, 0x0f, 0x80, 0x1c, 0x07, 0xe0, 0x03, 0xf1, 0xf8, 0x07, 0xff, 0xc3, 0xe0, 0x7e, 0x7e, + 0x03, 0xe3, 0xff, 0xfe, 0x7e, 0x00, 0x00, 0xf8, 0xfc, 0x0f, 0xdf, 0x80, 0x03, 0xff, 0xc0, 0x00, + 0x39, 0xff, 0x07, 0xc0, 0x1c, 0x03, 0xf0, 0x03, 0xf1, 0xf8, 0x1f, 0xff, 0xc3, 0xe0, 0x7e, 0x7e, + 0x03, 0xe3, 0xff, 0xfe, 0x7f, 0x00, 0x00, 0xf8, 0xf8, 0x0f, 0xdf, 0x80, 0x03, 0xff, 0xe0, 0x00, + 0x39, 0xfe, 0x23, 0xe0, 0x1c, 0x03, 0xf0, 0x07, 0xf1, 0xf8, 0x1f, 0xff, 0xc3, 0xe0, 0x7e, 0x7f, + 0x07, 0xe3, 0xf0, 0x00, 0x3f, 0x00, 0x40, 0xf8, 0xfc, 0x0f, 0xcf, 0x80, 0x03, 0xff, 0xe0, 0x00, + 0x3c, 0xfc, 0x61, 0xf0, 0x3c, 0x03, 0xf8, 0x0f, 0xe1, 0xf8, 0x3f, 0x0f, 0xc3, 0xe0, 0x7e, 0x3f, + 0x8f, 0xe3, 0xf0, 0x00, 0x3f, 0x80, 0xe0, 0xf8, 0xfc, 0x1f, 0x8f, 0xc1, 0x83, 0xff, 0xf0, 0x00, + 0x3c, 0xf8, 0xe0, 0xf8, 0x38, 0x01, 0xff, 0x3f, 0xe1, 0xf8, 0x3f, 0x0f, 0xc3, 0xe0, 0x7e, 0x3f, + 0xff, 0xe1, 0xf8, 0x30, 0x1f, 0xe3, 0xf0, 0xf8, 0x7e, 0x3f, 0x8f, 0xe3, 0xe3, 0xf3, 0xf8, 0x00, + 0x1e, 0x71, 0xe4, 0x7c, 0x78, 0x00, 0xff, 0xff, 0xc1, 0xf8, 0x3f, 0x1f, 0xc3, 0xe0, 0x7e, 0x1f, + 0xff, 0xe1, 0xff, 0xf8, 0x1f, 0xff, 0xf8, 0xf8, 0x7f, 0xff, 0x87, 0xff, 0xf3, 0xf1, 0xfc, 0x00, + 0x1e, 0x43, 0xe6, 0x3c, 0x78, 0x00, 0x7f, 0xff, 0x81, 0xf8, 0x1f, 0xff, 0xc3, 0xe0, 0x7e, 0x0f, + 0xff, 0xe0, 0xff, 0xfc, 0x0f, 0xff, 0xf0, 0xf8, 0x3f, 0xff, 0x03, 0xff, 0xe3, 0xf0, 0xfe, 0x00, + 0x0f, 0x07, 0xe7, 0x1c, 0xf0, 0x00, 0x3f, 0xff, 0x01, 0xf8, 0x1f, 0xff, 0xc3, 0xe0, 0x7e, 0x03, + 0xf3, 0xe0, 0x7f, 0xf8, 0x03, 0xff, 0xe0, 0xf8, 0x1f, 0xfe, 0x01, 0xff, 0xc3, 0xf0, 0x7e, 0x00, + 0x0f, 0x8f, 0xe7, 0x81, 0xe0, 0x00, 0x0f, 0xfc, 0x01, 0xf8, 0x07, 0xe7, 0xc3, 0xe0, 0x7e, 0x00, + 0x03, 0xe0, 0x1f, 0xf0, 0x01, 0xff, 0x80, 0xf8, 0x07, 0xf8, 0x00, 0x7f, 0x83, 0xf0, 0x7f, 0x00, + 0x07, 0xc7, 0xe7, 0xe3, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xe3, 0xe7, 0x87, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x1f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xf0, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0xc3, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + diff --git a/src/epd.cpp b/src/epd.cpp index 04b4e9f..65a50cf 100644 --- a/src/epd.cpp +++ b/src/epd.cpp @@ -30,7 +30,13 @@ void setupDisplay() uint16_t y = ((display.height() - tbh) / 2) - tby; display.fillScreen(GxEPD_BLACK); display.setCursor(x, y); - display.print("OrangeBTClock"); +// display.print("OrangeBTClock"); + +// display.drawImage(epd_bitmap_allArray[0], GxEPD_WHITE, 0,0 250,37); + + int xPos = (display.width() - 250) / 2; + int yPos = (display.height() - 37) / 2; + display.drawBitmap(xPos,yPos, epd_bitmap_oclogo, 250, 37, GxEPD_WHITE); display.display(false); // display.fillScreen(GxEPD_WHITE); @@ -47,11 +53,11 @@ void setupDisplay() // display.display(true); - display.setRotation(1); - // display.fillRect(0, row1, display.width(), 54, GxEPD_BLACK); - display.displayWindow(0, row1, display.width(), row2); + // display.setRotation(1); + // // display.fillRect(0, row1, display.width(), 54, GxEPD_BLACK); + // display.displayWindow(0, row1, display.width(), row2); - display.display(false); + // display.display(false); // display.fillRect(0, row2, display.width(), 54, GxEPD_BLACK); // display.displayWindow(0, row2, display.width(), 54); diff --git a/src/epd.hpp b/src/epd.hpp index ab02ff1..44d00b2 100644 --- a/src/epd.hpp +++ b/src/epd.hpp @@ -2,7 +2,7 @@ #include "shared.hpp" #include "fonts/fonts.hpp" - +#include "bitmap.hpp" void setupDisplay(); void showSetupText(String t); diff --git a/src/main.cpp b/src/main.cpp index 3537df6..7cb1674 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,17 +43,17 @@ void setup() // setCpuFrequencyMhz(40); Serial.begin(115200); - #ifndef IS_ORANGECLOCK + //#ifndef IS_ORANGECLOCK pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); - #else - FastLED.addLeds(leds, NUM_LEDS); + //#else + FastLED.addLeds(leds, NUM_LEDS); leds[0] = CRGB::DarkOrange; leds[1] = CRGB::OrangeRed; FastLED.show(); - #endif + //#endif setupPreferences(); setupDisplay(); @@ -76,12 +76,10 @@ void setup() FastLED.show(); delay(100); - + #endif display.setFullWindow(); display.clearScreen(GxEPD_WHITE); display.display(true); - - #endif } void loop() From 88e82797a698dc8ab529ead685b893177427548d Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Tue, 19 Mar 2024 01:37:58 +0100 Subject: [PATCH 13/22] Bugfix in webui --- data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data b/data index 2e9164f..b7c4c5f 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 2e9164fc8f2756ef55520978cf70214bde17364d +Subproject commit b7c4c5ffbadcde44d6578e6a9a3498d40bf8dee6 From 730cc4338fd361983aa72ccf4bb579e908bfca96 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sat, 30 Mar 2024 14:16:24 +0100 Subject: [PATCH 14/22] Improved OrangeClock board defintion, add button handler --- boards/orangeclock.json | 2 +- src/config.cpp | 62 +++++++++++++++++++++++++++++++++++++++-- src/config.hpp | 4 ++- src/main.cpp | 53 +++++++++++++++++++---------------- src/shared.cpp | 1 + src/shared.hpp | 5 ++++ 6 files changed, 99 insertions(+), 28 deletions(-) diff --git a/boards/orangeclock.json b/boards/orangeclock.json index c852a39..7ea7754 100644 --- a/boards/orangeclock.json +++ b/boards/orangeclock.json @@ -11,7 +11,7 @@ "-DARDUINO_ORANGECLOCK", "-DARDUINO_ESP32S3_DEV", "-DIS_ORANGECLOCK", - "-DARDUINO_USB_MODE=1", + "-DARDUINO_USB_MODE=0", "-DARDUINO_RUNNING_CORE=1", "-DARDUINO_EVENT_RUNNING_CORE=1", "-DARDUINO_USB_CDC_ON_BOOT=1" diff --git a/src/config.cpp b/src/config.cpp index d676389..46c1e7b 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -6,6 +6,9 @@ const char *ntpServer = "pool.ntp.org"; // const long gmtOffset_sec = 0; const int daylightOffset_sec = 3600; TaskHandle_t OTAHandle = NULL; +SemaphoreHandle_t xButtonSemaphore = NULL; +const TickType_t debounceDelay = pdMS_TO_TICKS(500); +TickType_t lastButtonPressTime = 0; #define STA_SSID "" #define STA_PASS "" @@ -92,7 +95,7 @@ void setupWifi() WiFiManager wm; - #ifndef ARDUINO_ORANGECLOCK +#ifndef ARDUINO_ORANGECLOCK // Touch pin 14 to reset if (touchRead(14) > 9000) { @@ -109,7 +112,7 @@ void setupWifi() wm.resetSettings(); } } - #endif +#endif String softAP_SSID = String("OrangeBTClock"); @@ -264,6 +267,42 @@ void OTAUpdateTask(void *pvParameters) } } +void HandleButtonTask(void *pvParameters) +{ + for (;;) + { + if (xSemaphoreTake(xButtonSemaphore, portMAX_DELAY) == pdTRUE) + { + TickType_t currentTime = xTaskGetTickCount(); + if ((currentTime - lastButtonPressTime) >= debounceDelay) + { + lastButtonPressTime = currentTime; + + Serial.println("Button Pressed"); + + leds[0] = CRGB::SkyBlue; + leds[1] = CRGB::Black; + + FastLED.show(); + + vTaskDelay(100); + + leds[0] = CRGB::Black; + leds[1] = CRGB::DarkOrange; + + FastLED.show(); + + vTaskDelay(100); + + leds[0] = CRGB::Black; + leds[1] = CRGB::Black; + + FastLED.show(); + } + } + } +} + char getCurrencyIcon() { char ret; @@ -286,4 +325,23 @@ char getCurrencyIcon() } return ret; +} + +void IRAM_ATTR onButtonPress() +{ + xSemaphoreGiveFromISR(xButtonSemaphore, NULL); +} + +void setupButtonISR() +{ + xButtonSemaphore = xSemaphoreCreateBinary(); + + xTaskCreatePinnedToCore( + HandleButtonTask, // Task function + "Button Task", // Task name + 2048, // Stack size (bytes) + NULL, // Task parameters + 1, // Priority (1 is default) + NULL, // Task handle + 0); // Core to run the task (0 or 1) } \ No newline at end of file diff --git a/src/config.hpp b/src/config.hpp index c4936e4..fc4f7ca 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -21,4 +21,6 @@ void wakeModemSleep(); void setModemSleep(); bool inPowerSaveMode(); -char getCurrencyIcon(); \ No newline at end of file +char getCurrencyIcon(); +void IRAM_ATTR onButtonPress(); +void setupButtonISR(); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 7cb1674..ce78854 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,7 +8,6 @@ #include "config.hpp" #include "webserver.hpp" #include -#include #define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */ #define TIME_TO_SLEEP 5 /* Time ESP32 will go to sleep (in seconds) */ @@ -35,25 +34,31 @@ uint currentPrice = 0; String currentBlock = ""; String currentFees = ""; -#define NUM_LEDS 2 CRGB leds[NUM_LEDS]; void setup() { // setCpuFrequencyMhz(40); Serial.begin(115200); - - //#ifndef IS_ORANGECLOCK + delay(2500); + Serial.println("Hello"); + #ifndef IS_ORANGECLOCK pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); - //#else - FastLED.addLeds(leds, NUM_LEDS); - leds[0] = CRGB::DarkOrange; + #else + + pinMode(BUTTON_PIN, INPUT_PULLUP); + attachInterrupt(BUTTON_PIN, onButtonPress, FALLING); + + FastLED.addLeds(leds, NUM_LEDS); + leds[0] = CRGB::GreenYellow; leds[1] = CRGB::OrangeRed; FastLED.show(); - //#endif + + setupButtonISR(); + #endif setupPreferences(); setupDisplay(); @@ -102,24 +107,24 @@ void loop() // - IPAddress res; - uint result = WiFi.hostByName("mempool.space", res); + // IPAddress res; + // uint result = WiFi.hostByName("mempool.space", res); - if (result >= 0) - { - Serial.print("SUCCESS!"); - Serial.println(res.toString()); - } - else - { - WiFi.reconnect(); + // if (result >= 0) + // { + // Serial.print("SUCCESS!"); + // Serial.println(res.toString()); + // } + // else + // { + // WiFi.reconnect(); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print('.'); - delay(1000); - } - } + // while (WiFi.status() != WL_CONNECTED) + // { + // Serial.print('.'); + // delay(1000); + // } + // } for (uint i = 1; i <= 3; i++) { diff --git a/src/shared.cpp b/src/shared.cpp index 2547a42..a7dd264 100644 --- a/src/shared.cpp +++ b/src/shared.cpp @@ -1,2 +1,3 @@ #include "shared.hpp" +volatile bool buttonPressed = false; diff --git a/src/shared.hpp b/src/shared.hpp index ec281c6..4321542 100644 --- a/src/shared.hpp +++ b/src/shared.hpp @@ -7,6 +7,9 @@ #include #include "utils.hpp" #include "fonts/fonts.hpp" +#include +#include +#include #ifdef VERSION_EPD_2_13 #define EPD_CLASS GxEPD2_213_B74 @@ -101,3 +104,5 @@ extern char currentIcon1; extern char currentIcon2; extern char currentIcon3; +extern CRGB leds[NUM_LEDS]; +extern volatile bool buttonPressed; \ No newline at end of file From 486a1b9be2f4eefb536c1c5637ec4be692ad4f27 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sat, 30 Mar 2024 15:02:02 +0100 Subject: [PATCH 15/22] Pin settings in platformio.ini --- platformio.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 613f146..09218a2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -69,4 +69,6 @@ board = orangeclock build_flags = ${btclock_base.build_flags} -D VERSION_EPD_2_9 - -D IS_ORANGECLOCK \ No newline at end of file + -D IS_ORANGECLOCK + -D BUTTON_PIN=45 + -D NUM_LEDS=2 \ No newline at end of file From 0a3f747d002c1183b3a3acd15a80384cc6dfe8e9 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sat, 30 Mar 2024 15:24:10 +0100 Subject: [PATCH 16/22] Fix environments without leds --- src/config.cpp | 2 ++ src/main.cpp | 2 ++ src/shared.hpp | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/config.cpp b/src/config.cpp index 46c1e7b..a9ebdf8 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -280,6 +280,7 @@ void HandleButtonTask(void *pvParameters) Serial.println("Button Pressed"); + #ifdef NUM_LEDS leds[0] = CRGB::SkyBlue; leds[1] = CRGB::Black; @@ -298,6 +299,7 @@ void HandleButtonTask(void *pvParameters) leds[1] = CRGB::Black; FastLED.show(); + #endif } } } diff --git a/src/main.cpp b/src/main.cpp index ce78854..2647688 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,7 +34,9 @@ uint currentPrice = 0; String currentBlock = ""; String currentFees = ""; +#ifdef NUM_LEDS CRGB leds[NUM_LEDS]; +#endif void setup() { diff --git a/src/shared.hpp b/src/shared.hpp index 4321542..2d99256 100644 --- a/src/shared.hpp +++ b/src/shared.hpp @@ -104,5 +104,7 @@ extern char currentIcon1; extern char currentIcon2; extern char currentIcon3; +#ifdef NUM_LEDS extern CRGB leds[NUM_LEDS]; +#endif extern volatile bool buttonPressed; \ No newline at end of file From f80c314247af673bce101f38dfea47d9d57cd846 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sat, 30 Mar 2024 16:33:45 +0100 Subject: [PATCH 17/22] Add IP address to splash screen --- src/config.cpp | 1 + src/epd.cpp | 33 ++++++++++++++++++++++++++------- src/epd.hpp | 3 ++- src/main.cpp | 2 -- src/shared.hpp | 1 + 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index a9ebdf8..2486370 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -154,6 +154,7 @@ void setupWifi() Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); + epdShowIp(); // WiFi.setTxPower(WIFI_POWER_8_5dBm); // enableWiFi(); } diff --git a/src/epd.cpp b/src/epd.cpp index 65a50cf..ec23ee6 100644 --- a/src/epd.cpp +++ b/src/epd.cpp @@ -22,14 +22,14 @@ void setupDisplay() display.setRotation(1); display.setFont(&Antonio_SemiBold20pt7b); display.setTextColor(GxEPD_WHITE); - int16_t tbx, tby; - uint16_t tbw, tbh; - display.getTextBounds("OrangeBTClock", 0, 0, &tbx, &tby, &tbw, &tbh); - // center the bounding box by transposition of the origin: - uint16_t x = ((display.width() - tbw) / 2) - tbx; - uint16_t y = ((display.height() - tbh) / 2) - tby; + // int16_t tbx, tby; + // uint16_t tbw, tbh; + // display.getTextBounds("OrangeBTClock", 0, 0, &tbx, &tby, &tbw, &tbh); + // // center the bounding box by transposition of the origin: + // uint16_t x = ((display.width() - tbw) / 2) - tbx; + // uint16_t y = ((display.height() - tbh) / 2) - tby; display.fillScreen(GxEPD_BLACK); - display.setCursor(x, y); + // display.setCursor(x, y); // display.print("OrangeBTClock"); // display.drawImage(epd_bitmap_allArray[0], GxEPD_WHITE, 0,0 250,37); @@ -39,6 +39,9 @@ void setupDisplay() display.drawBitmap(xPos,yPos, epd_bitmap_oclogo, 250, 37, GxEPD_WHITE); display.display(false); + display.setCursor(0, 37); + + // display.fillScreen(GxEPD_WHITE); // display.drawLine(0, 10, display.width(), 10, GxEPD_BLACK); // display.drawLine(0, row2, display.width(), row2, GxEPD_BLACK); @@ -70,6 +73,22 @@ void setupDisplay() // display.display(true); } +void epdShowIp() { + display.setRotation(1); + display.setFont(&LibreFranklin_SemiBold10pt7b); + display.setTextColor(GxEPD_WHITE); + String ipStr = WiFi.localIP().toString(); + int16_t tbx, tby; + uint16_t tbw, tbh; + display.getTextBounds(ipStr, 0, 0, &tbx, &tby, &tbw, &tbh); + // center the bounding box by transposition of the origin: + uint16_t x = ((display.width() - tbw) / 2) - tbx; + uint16_t y = ((display.height() - tbh) / 2) - tby + 37; + display.setCursor(x, y); + display.println(WiFi.localIP()); + display.display(true); +} + void updateRow2(String c, char icon) { if (c.equals(currentRow2) && icon == currentIcon2) diff --git a/src/epd.hpp b/src/epd.hpp index 44d00b2..c566001 100644 --- a/src/epd.hpp +++ b/src/epd.hpp @@ -9,4 +9,5 @@ void showSetupText(String t); void updateRow1(String c, char icon); void updateRow2(String c, char icon); void updateRow3(String c, char icon); -void updateRows(String row1Content, String row2Content, String row3Content); \ No newline at end of file +void updateRows(String row1Content, String row2Content, String row3Content); +void epdShowIp(); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 2647688..fe130cc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,8 +42,6 @@ void setup() { // setCpuFrequencyMhz(40); Serial.begin(115200); - delay(2500); - Serial.println("Hello"); #ifndef IS_ORANGECLOCK pinMode(LED_BUILTIN, OUTPUT); diff --git a/src/shared.hpp b/src/shared.hpp index 2d99256..f0264e8 100644 --- a/src/shared.hpp +++ b/src/shared.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #ifdef VERSION_EPD_2_13 #define EPD_CLASS GxEPD2_213_B74 From 99c34d029f583b5b65f954871f37c77c3709008e Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Thu, 19 Dec 2024 17:04:16 +0100 Subject: [PATCH 18/22] Updates and add Forgejo CI action --- .forgejo/workflows/push.yaml | 139 +++++++++++++++++++++++++++++++++++ platformio.ini | 9 +-- src/config.cpp | 3 - src/config.hpp | 2 +- src/main.cpp | 8 +- src/shared.hpp | 4 +- src/utils.hpp | 1 + 7 files changed, 151 insertions(+), 15 deletions(-) create mode 100644 .forgejo/workflows/push.yaml diff --git a/.forgejo/workflows/push.yaml b/.forgejo/workflows/push.yaml new file mode 100644 index 0000000..8cf5442 --- /dev/null +++ b/.forgejo/workflows/push.yaml @@ -0,0 +1,139 @@ +name: "BTClock CI" + +on: + push: + tags: + - "*" + workflow_dispatch: + +jobs: + build: + runs-on: docker + container: + image: ghcr.io/catthehacker/ubuntu:js-22.04 + permissions: + contents: write + checks: write + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - uses: actions/setup-node@v4 + with: + node-version: lts/* + cache: yarn + cache-dependency-path: "**/yarn.lock" + - uses: actions/cache@v4 + with: + path: | + ~/.cache/pip + ~/.platformio/.cache + ~/data/node_modules + .pio + data/node_modules + key: ${{ runner.os }}-pio + - uses: actions/setup-python@v5 + with: + python-version: "3.9" + cache: "pip" + - 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: Build BTClock firmware + shell: bash + run: pio run + - name: Build BTClock filesystem + shell: bash + run: pio run --target buildfs + - name: Copy bootloader to output folder + run: cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin .pio + - name: Upload artifacts + uses: https://code.forgejo.org/forgejo/upload-artifact@v4 + with: + include-hidden-files: true + retention-days: 1 + name: prepared-outputs + path: .pio/**/*.bin + merge: + runs-on: docker + container: + image: ghcr.io/catthehacker/ubuntu:js-22.04 + permissions: + contents: write + checks: write + needs: build + continue-on-error: true + strategy: + matrix: + chip: + - name: lolin_s2_mini + version: esp32s2 + - name: lolin_s3_mini + version: esp32s3 + - name: orangeclock + version: esp32s3 + epd_variant: [213epd, 29epd] + exclude: + - chip: orangeclock + epd_variant: 213epd + steps: + - uses: https://code.forgejo.org/forgejo/download-artifact@v4 + with: + name: prepared-outputs + path: .pio + - 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.chip.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 .pio/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 firmware + shell: bash + run: shasum -a 256 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/firmware.bin | awk '{print $1}' > ${{ matrix.chip.name }}_${{ matrix.epd_variant }}/${{ matrix.chip.name }}_${{ matrix.epd_variant }}_firmware.bin.sha256 + + - 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: Upload artifacts + uses: https://code.forgejo.org/forgejo/upload-artifact@v4 + 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: docker + permissions: + contents: write + checks: write + needs: merge + steps: + - name: Download matrix outputs + uses: https://code.forgejo.org/forgejo/download-artifact@v4 + with: + pattern: build-* + merge-multiple: false + path: temp + - name: Copy files + run: | + mkdir -p release + find temp -type f \( -name "*.bin" -o -name "*.sha256" \) -exec cp -f {} release/ \; + - name: Create release + uses: https://code.forgejo.org/actions/forgejo-release@v2.4.0 + with: + url: "https://git.btclock.dev" + repo: "${{ github.repository }}" + direction: upload + tag: "${{ github.ref_name }}" + sha: "${{ github.sha }}" + release-dir: release + token: ${{ secrets.TOKEN }} + override: ${{ github.ref_type != 'tag' && github.ref_name != 'main' }} + prerelease: ${{ github.ref_type != 'tag' && github.ref_name != 'main' }} + release-notes-assistant: false diff --git a/platformio.ini b/platformio.ini index 09218a2..080de32 100644 --- a/platformio.ini +++ b/platformio.ini @@ -24,12 +24,11 @@ build_flags = !python scripts/git_rev.py -DLAST_BUILD_TIME=$UNIX_TIME lib_deps = - zinggjm/GxEPD2@^1.5.6 + zinggjm/GxEPD2@^1.6.1 https://github.com/tzapu/WiFiManager.git#v2.0.17 - bblanchon/ArduinoJson@^7.0.3 - mathieucarbou/ESP Async WebServer - gilmaimon/ArduinoWebsockets@^0.5.3 - fastled/FastLED@^3.6.0 + bblanchon/ArduinoJson@^7.2.1 + mathieucarbou/ESP Async WebServer@^3.0.6 + fastled/FastLED@^3.9.6 [env:lolin_s2_mini] extends = btclock_base board = lolin_s2_mini diff --git a/src/config.cpp b/src/config.cpp index 2486370..3788a9a 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -90,7 +90,6 @@ void setupWifi() randomSeed(seed); // WiFi.begin(, "); - WiFi.setAutoConnect(true); WiFi.setAutoReconnect(true); WiFiManager wm; @@ -181,7 +180,6 @@ void wakeModemSleep() void enableWiFi() { - adc_power_on(); delay(200); WiFi.disconnect(false); // Reconnect the network @@ -206,7 +204,6 @@ void enableWiFi() void disableWiFi() { - adc_power_off(); WiFi.disconnect(true); // Disconnect from the network WiFi.mode(WIFI_OFF); // Switch WiFi off Serial.println(""); diff --git a/src/config.hpp b/src/config.hpp index fc4f7ca..f7ead4a 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -3,11 +3,11 @@ #include #include #include "shared.hpp" -#include "driver/adc.h" #include #include #include "epd.hpp" #include +#include void enableWiFi(); void disableWiFi(); diff --git a/src/main.cpp b/src/main.cpp index fe130cc..b3c25fc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include "shared.hpp" #include "epd.hpp" @@ -29,7 +29,7 @@ typedef void (*MethodPtr)(String, char); MethodPtr methods[] = {nullptr, updateRow1, updateRow2, updateRow3}; -WiFiClientSecure client; +WiFiClient client; uint currentPrice = 0; String currentBlock = ""; String currentFees = ""; @@ -71,7 +71,7 @@ void setup() setupWebserver(); setupOTA(); } - client.setInsecure(); + // client.setInsecure(); #ifndef IS_ORANGECLOCK digitalWrite(LED_BUILTIN, LOW); @@ -103,7 +103,7 @@ void loop() return; } - client.setInsecure(); + // client.setInsecure(); // diff --git a/src/shared.hpp b/src/shared.hpp index f0264e8..dd17f5b 100644 --- a/src/shared.hpp +++ b/src/shared.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include #include @@ -92,7 +92,7 @@ const int LINE_DATE = 100; #define CURRENCY_AUD "AUD" #define CURRENCY_JPY "JPY" -extern WiFiClientSecure client; +extern WiFiClient client; extern GxEPD2_BW display; extern Preferences preferences; extern bool isUpdating; diff --git a/src/utils.hpp b/src/utils.hpp index 8c32d47..80419f0 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -4,6 +4,7 @@ #include #include #include "shared.hpp" +#include namespace ArduinoJson { template <> From d9063b606a0ce9b03e32c4311b8e2cef188abb21 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Thu, 19 Dec 2024 17:06:25 +0100 Subject: [PATCH 19/22] Update webUI submodule location --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index f9d84d8..55b6d4f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "data"] path = data - url = https://github.com/btclock/oc-webui.git + url = https://git.btclock.dev/btclock/oc-webui.git From 7702398b36beae1523475fb1eda63b9295fe70bb Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Thu, 19 Dec 2024 17:10:23 +0100 Subject: [PATCH 20/22] Fix Forgejo CI --- .forgejo/workflows/push.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.forgejo/workflows/push.yaml b/.forgejo/workflows/push.yaml index 8cf5442..cabc4e2 100644 --- a/.forgejo/workflows/push.yaml +++ b/.forgejo/workflows/push.yaml @@ -43,9 +43,6 @@ jobs: - 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: Build BTClock firmware shell: bash run: pio run @@ -81,7 +78,7 @@ jobs: version: esp32s3 epd_variant: [213epd, 29epd] exclude: - - chip: orangeclock + - chip: { name: orangeclock, version: esp32s3 } epd_variant: 213epd steps: - uses: https://code.forgejo.org/forgejo/download-artifact@v4 From fc868a2d81a733a649686bd98237bbabf62d6b16 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Thu, 19 Dec 2024 17:17:15 +0100 Subject: [PATCH 21/22] Remove unneeded import --- src/config.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/config.hpp b/src/config.hpp index f7ead4a..8a1aaa4 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -7,7 +7,6 @@ #include #include "epd.hpp" #include -#include void enableWiFi(); void disableWiFi(); From 63827d3ab67c00e8bd867a0986f7f2607f9f8670 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Thu, 19 Dec 2024 19:51:57 +0100 Subject: [PATCH 22/22] Use correct Arduino framework version --- data | 2 +- platformio.ini | 1 + src/main.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/data b/data index b7c4c5f..8332fec 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit b7c4c5ffbadcde44d6578e6a9a3498d40bf8dee6 +Subproject commit 8332fec4a1ec0045d91f063617bb441914e7b67a diff --git a/platformio.ini b/platformio.ini index 080de32..af5bf0f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,6 +15,7 @@ default_envs = lolin_s2_mini_213epd, lolin_s2_mini_29epd, lolin_s3_mini_213epd, [btclock_base] platform = espressif32 framework = arduino +platform_packages = platformio/framework-arduinoespressif32 monitor_speed = 115200 monitor_filters = esp32_exception_decoder, colorize board_build.filesystem = littlefs diff --git a/src/main.cpp b/src/main.cpp index b3c25fc..3cf7b1b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include "shared.hpp" #include "epd.hpp"