Compare commits
No commits in common. "main" and "feature/V2_datasource" have entirely different histories.
main
...
feature/V2
82 changed files with 1949 additions and 5470 deletions
|
@ -1,190 +0,0 @@
|
|||
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_s3_mini
|
||||
version: esp32s3
|
||||
- name: btclock_rev_b
|
||||
version: esp32s3
|
||||
- name: btclock_v8
|
||||
version: esp32s3
|
||||
epd_variant: [213epd, 29epd]
|
||||
exclude:
|
||||
- chip: { name: btclock_rev_b, version: esp32s3 }
|
||||
epd_variant: 29epd
|
||||
- chip: { name: btclock_v8, version: esp32s3 }
|
||||
epd_variant: 29epd
|
||||
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
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p ${{ matrix.chip.name }}_${{ matrix.epd_variant }}
|
||||
if [ "${{ matrix.chip.name }}" == "btclock_v8" ]; then
|
||||
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 \
|
||||
--flash_freq 80m \
|
||||
--flash_size 16MB \
|
||||
0x0000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/bootloader.bin \
|
||||
0x8000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/partitions.bin \
|
||||
0xe000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/ota_data_initial.bin \
|
||||
0x10000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/firmware.bin \
|
||||
0xDF0000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/littlefs_16MB.bin
|
||||
elif [ "${{ matrix.chip.name }}" == "btclock_rev_b" ]; then
|
||||
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 \
|
||||
--flash_freq 80m \
|
||||
--flash_size 8MB \
|
||||
0x0000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/bootloader.bin \
|
||||
0x8000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/partitions.bin \
|
||||
0xe000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/ota_data_initial.bin \
|
||||
0x10000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/firmware.bin \
|
||||
0x6F0000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/littlefs_8MB.bin;
|
||||
else
|
||||
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/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/ota_data_initial.bin \
|
||||
0x10000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/firmware.bin \
|
||||
0x380000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/littlefs_4MB.bin
|
||||
# Adjust the offset for littlefs or other files as needed for the original case
|
||||
fi
|
||||
|
||||
- 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
|
||||
shell: bash
|
||||
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 }}.bin.sha256
|
||||
|
||||
- name: Create checksum for littlefs partition
|
||||
shell: bash
|
||||
run: |
|
||||
fs_file=$(find .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }} -name "littlefs*.bin")
|
||||
echo $fs_file
|
||||
fs_name=$(basename "$fs_file")
|
||||
shasum -a 256 "$fs_file" | awk '{print $1}' > "${{ matrix.chip.name }}_${{ matrix.epd_variant }}/${fs_name}.sha256"
|
||||
cat "${{ matrix.chip.name }}_${{ matrix.epd_variant }}/${fs_name}.sha256"
|
||||
- name: Copy all artifacts to output folder
|
||||
run: cp .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/*.bin .pio/boot_app0.bin ${{ matrix.chip.name }}_${{ matrix.epd_variant }}
|
||||
|
||||
- name: Create OTA binary file
|
||||
run: mv ${{ matrix.chip.name }}_${{ matrix.epd_variant }}/firmware.bin ${{ matrix.chip.name }}_${{ matrix.epd_variant }}/${{ matrix.chip.name }}_${{ matrix.epd_variant }}_firmware.bin
|
||||
- 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
|
60
.github/workflows/tagging.yml
vendored
60
.github/workflows/tagging.yml
vendored
|
@ -1,9 +1,9 @@
|
|||
name: BTClock CI
|
||||
|
||||
on:
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -40,14 +40,10 @@ jobs:
|
|||
version: esp32s3
|
||||
- name: btclock_rev_b
|
||||
version: esp32s3
|
||||
- name: btclock_v8
|
||||
version: esp32s3
|
||||
epd_variant: [213epd, 29epd]
|
||||
exclude:
|
||||
- chip: {name: btclock_rev_b, version: esp32s3}
|
||||
epd_variant: 29epd
|
||||
- chip: {name: btclock_v8, version: esp32s3}
|
||||
epd_variant: 29epd
|
||||
- chip: btclock_rev_b
|
||||
epd_variant: 29epd
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
|
@ -55,44 +51,12 @@ jobs:
|
|||
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 merged firmware binary
|
||||
run: |
|
||||
if [ "${{ matrix.chip.name }}" == "btclock_v8" ]; then
|
||||
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 \
|
||||
--flash_freq 80m \
|
||||
--flash_size 16MB \
|
||||
0x0000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/bootloader.bin \
|
||||
0x8000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/partitions.bin \
|
||||
0xe000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/ota_data_initial.bin \
|
||||
0x10000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/firmware.bin \
|
||||
0x810000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/littlefs.bin;
|
||||
else
|
||||
# Original command for other cases
|
||||
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/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/ota_data_initial.bin \
|
||||
0x10000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/firmware.bin \
|
||||
0x369000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/littlefs.bin
|
||||
# Adjust the offset for littlefs or other files as needed for the original case
|
||||
fi
|
||||
|
||||
- name: Create checksum for firmware
|
||||
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 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 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 }}.bin.sha256
|
||||
|
||||
- name: Create checksum for littlefs partition
|
||||
run: shasum -a 256 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/littlefs.bin | awk '{print $1}' > ${{ matrix.chip.name }}_${{ matrix.epd_variant }}/littlefs.bin.sha256
|
||||
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: Copy all artifacts to output folder
|
||||
run: cp .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/*.bin .pio/boot_app0.bin ${{ matrix.chip.name }}_${{ matrix.epd_variant }}
|
||||
|
@ -121,10 +85,10 @@ jobs:
|
|||
merge-multiple: false
|
||||
- name: Write commit hash to file
|
||||
run: echo $GITHUB_SHA > commit.txt
|
||||
|
||||
|
||||
- name: Write build date to file
|
||||
run: echo "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" > date.txt
|
||||
|
||||
|
||||
- name: Create release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
|
@ -147,8 +111,8 @@ jobs:
|
|||
with:
|
||||
source-directory: .
|
||||
target-directory: firmware_v3/
|
||||
destination-github-username: "btclock"
|
||||
destination-repository-name: "web-flasher"
|
||||
destination-github-username: 'btclock'
|
||||
destination-repository-name: 'web-flasher'
|
||||
target-branch: main
|
||||
user-name: ${{github.actor}}
|
||||
user-email: ${{github.actor}}@users.noreply.github.com
|
||||
user-email: ${{github.actor}}@users.noreply.github.com
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -10,5 +10,4 @@ data/.yarn
|
|||
data/node_modules
|
||||
node_modules
|
||||
.DS_Store
|
||||
*.bin
|
||||
ci/cache
|
||||
*.bin
|
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -1,3 +1,3 @@
|
|||
[submodule "data"]
|
||||
path = data
|
||||
url = https://git.btclock.dev/btclock/webui.git
|
||||
url = https://github.com/btclock/webui.git
|
||||
|
|
44
README.md
44
README.md
|
@ -1,8 +1,6 @@
|
|||
# BTClock v3
|
||||
|
||||
[![Latest release](https://git.btclock.dev/btclock/btclock_v3/badges/release.svg)](https://git.btclock.dev/btclock/btclock_v3/releases/latest)
|
||||
|
||||
[![BTClock CI](https://git.btclock.dev/btclock/btclock_v3/badges/workflows/push.yaml/badge.svg)](https://git.btclock.dev/btclock/btclock_v3/actions?workflow=push.yaml&actor=0&status=0)
|
||||
[![BTClock CI](https://github.com/btclock/btclock_v3/actions/workflows/tagging.yml/badge.svg)](https://github.com/btclock/btclock_v3/actions/workflows/tagging.yml)
|
||||
|
||||
Software for the BTClock project.
|
||||
|
||||
|
@ -14,46 +12,12 @@ Biggest differences with v2 are:
|
|||
- Added market capitalization screen
|
||||
- LED flash on new block (and focus to block height screen on new block)
|
||||
|
||||
New features:
|
||||
- BitAxe integration
|
||||
- Nostr Zap notifier
|
||||
- Multiple mining pool stats integrations
|
||||
|
||||
"Steal focus on new block" means that when a new block is mined, the display will switch to the block height screen if it's not on it already.
|
||||
|
||||
See the [docs](https://git.btclock.dev/btclock/docs) repo for more information and building instructions.
|
||||
Most [information](https://github.com/btclock/btclock_v2/wiki) about BTClock v2 is still valid for this version.
|
||||
|
||||
**NOTE**: The software assumes that the hardware is run in a controlled private network. ~~The Web UI and the OTA update mechanism are not password protected and accessible to anyone in the network. Also, since the device only fetches numbers through WebSockets it will skip server certificate verification to save resources.~~ Since 3.2.0 the WebUI is password protectable and all certificates are verified. OTA update mechanism is not password-protected.
|
||||
**NOTE**: The software assumes that the hardware is run in a controlled private network. The Web UI and the OTA update mechanism are not password protected and accessible to anyone in the network. Also, since the device only fetches numbers through WebSockets it will skip server certificate verification to save resources.
|
||||
|
||||
## Building
|
||||
|
||||
Use PlatformIO to build it yourself. Make sure you fetch the [WebUI](https://git.btclock.dev/btclock/webui) submodule.
|
||||
|
||||
|
||||
## Mining pool stats
|
||||
Enable mining pool stats by accessing your btclock's web UI (point a web browser at the device's IP address).
|
||||
|
||||
Under Settings -> Extra Features: toggle Enable Mining Pool Stats.
|
||||
|
||||
New options will appear. Select your mining pool and enter your pool username (Ocean) or api key (Braiins).
|
||||
|
||||
The Mining Pool Earnings screen displays:
|
||||
* Braiins: Today's mining reward thus far
|
||||
* Ocean: Your estimated earnings if the pool were to find a block right now
|
||||
|
||||
For solo mining pools, there are no earning estimations. Your username is the onchain withdrawal address, without the worker name.
|
||||
|
||||
|
||||
### Braiins Pool integration
|
||||
Create an API key based on the steps [here](https://academy.braiins.com/en/braiins-pool/monitoring/#api-configuration).
|
||||
|
||||
The key's permissions should be:
|
||||
* Web Access: no
|
||||
* API Access: yes
|
||||
* Access Permissions: Read-only
|
||||
|
||||
Copy the token that is created for the new key. Enter this as your "Mining Pool username or api key" in the btclock web UI.
|
||||
|
||||
|
||||
### Ocean integration
|
||||
Your "Mining Pool username" is just the onchain withdrawal address that you specify when pointing your miners at Ocean.
|
||||
Use PlatformIO to build it yourself. Make sure you fetch the [WebUI](https://github.com/btclock/webui) submodule.
|
|
@ -10,7 +10,7 @@
|
|||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_BTCLOCK",
|
||||
"-DARDUINO_ESP32S3_DEV",
|
||||
"-DIS_BTCLOCK_V8",
|
||||
"-DIS_BTCLOCK_S3",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1",
|
||||
|
@ -20,8 +20,8 @@
|
|||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"psram_type": "opi",
|
||||
"esp-idf": {
|
||||
"sdkconfig_path": "boards/sdkconfig.btclock_v8"
|
||||
"espidf": {
|
||||
"sdkconfig_path": "boards"
|
||||
},
|
||||
"hwids": [
|
||||
[
|
File diff suppressed because it is too large
Load diff
|
@ -1,15 +0,0 @@
|
|||
# Use the official Python 3.9 image as the base
|
||||
FROM python:3.9-slim
|
||||
|
||||
# Set the working directory
|
||||
WORKDIR /workspace
|
||||
|
||||
RUN apt-get update && apt-get install -y git
|
||||
|
||||
# Install PlatformIO
|
||||
RUN pip install platformio
|
||||
|
||||
WORKDIR /usr/src
|
||||
|
||||
CMD ["platformio", "run"]
|
||||
|
2
data
2
data
|
@ -1 +1 @@
|
|||
Subproject commit 924be8fc2eb02fe384a20b53da1a9fa3d8db8a05
|
||||
Subproject commit 2fffb3ef0284b4262ca97a81eb979259186604e5
|
|
@ -4,6 +4,6 @@ dependencies:
|
|||
source:
|
||||
type: idf
|
||||
version: 4.4.7
|
||||
manifest_hash: cd2f3ee15e776d949eb4ea4eddc8f39b30c2a7905050850eed01ab4928143cff
|
||||
manifest_hash: 615d994fdba8799111cf7825a85adb23ca6a2ae02b2335b53fde1188ff862f23
|
||||
target: esp32s3
|
||||
version: 1.0.0
|
||||
|
|
|
@ -24,7 +24,7 @@ std::array<std::string, NUM_SCREENS> parseBitaxeHashRate(std::string text)
|
|||
}
|
||||
|
||||
ret[NUM_SCREENS - 1] = "GH/S";
|
||||
ret[0] = "mdi:bitaxe";
|
||||
ret[0] = "BIT/AXE";
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ std::array<std::string, NUM_SCREENS> parseBitaxeBestDiff(std::string text)
|
|||
if (text.length() < NUM_SCREENS)
|
||||
{
|
||||
text.insert(text.begin(), NUM_SCREENS - text.length(), ' ');
|
||||
ret[0] = "mdi:bitaxe";
|
||||
ret[0] = "BIT/AXE";
|
||||
ret[1] = "mdi:rocket";
|
||||
firstIndex = 2;
|
||||
}
|
||||
|
|
|
@ -67,72 +67,33 @@ char getCurrencyChar(const std::string& input)
|
|||
return CURRENCY_USD; // Assuming USD is the default for unknown inputs
|
||||
}
|
||||
|
||||
std::array<std::string, NUM_SCREENS> parsePriceData(std::uint32_t price, char currencySymbol, bool useSuffixFormat, bool mowMode, bool shareDot)
|
||||
std::array<std::string, NUM_SCREENS> parsePriceData(std::uint32_t price, char currencySymbol, bool useSuffixFormat)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> ret;
|
||||
std::string priceString;
|
||||
if (std::to_string(price).length() >= NUM_SCREENS || useSuffixFormat)
|
||||
{
|
||||
int numScreens = shareDot || mowMode ? NUM_SCREENS - 1 : NUM_SCREENS - 2;
|
||||
priceString = getCurrencySymbol(currencySymbol) + formatNumberWithSuffix(price, numScreens, mowMode);
|
||||
priceString = getCurrencySymbol(currencySymbol) + formatNumberWithSuffix(price, NUM_SCREENS - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
priceString = getCurrencySymbol(currencySymbol) + std::to_string(price);
|
||||
}
|
||||
std::uint32_t firstIndex = 0;
|
||||
if ((shareDot && priceString.length() <= (NUM_SCREENS)) || priceString.length() < (NUM_SCREENS))
|
||||
if (priceString.length() < (NUM_SCREENS))
|
||||
{
|
||||
priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' ');
|
||||
|
||||
if (mowMode)
|
||||
{
|
||||
ret[0] = "MOW/UNITS";
|
||||
}
|
||||
else
|
||||
{
|
||||
ret[0] = "BTC/" + getCurrencyCode(currencySymbol);
|
||||
}
|
||||
ret[0] = "BTC/" + getCurrencyCode(currencySymbol);
|
||||
|
||||
|
||||
firstIndex = 1;
|
||||
}
|
||||
|
||||
size_t dotPosition = priceString.find('.');
|
||||
|
||||
if (shareDot && dotPosition != std::string::npos && dotPosition > 0)
|
||||
for (std::uint32_t i = firstIndex; i < NUM_SCREENS; i++)
|
||||
{
|
||||
std::vector<std::string> tempArray;
|
||||
if (dotPosition != std::string::npos && dotPosition > 0)
|
||||
{
|
||||
for (size_t i = 0; i < priceString.length(); ++i)
|
||||
{
|
||||
if (i == dotPosition - 1)
|
||||
{
|
||||
tempArray.push_back(std::string(1, priceString[i]) + ".");
|
||||
++i; // Skip the dot in the next iteration
|
||||
}
|
||||
else
|
||||
{
|
||||
tempArray.push_back(std::string(1, priceString[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Copy from tempArray to ret
|
||||
for (std::uint32_t i = firstIndex; i < NUM_SCREENS && i - firstIndex < tempArray.size(); ++i)
|
||||
{
|
||||
ret[i] = tempArray[i - firstIndex];
|
||||
}
|
||||
}
|
||||
ret[i] = priceString[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (std::uint32_t i = firstIndex; i < NUM_SCREENS; i++)
|
||||
{
|
||||
ret[i] = std::string(1, priceString[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -146,26 +107,9 @@ std::array<std::string, NUM_SCREENS> parseSatsPerCurrency(std::uint32_t price,ch
|
|||
|
||||
if (priceString.length() < (NUM_SCREENS))
|
||||
{
|
||||
// Check if price is greater than 1 billion
|
||||
if (price >= 100000000)
|
||||
{
|
||||
double satsPerCurrency = (1.0 / static_cast<double>(price)) * 1e8; // Calculate satoshis
|
||||
std::ostringstream oss;
|
||||
oss << std::fixed << std::setprecision(3) << satsPerCurrency; // Format with 3 decimal places
|
||||
priceString = oss.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
priceString = std::to_string(static_cast<int>(round(1.0 / static_cast<double>(price) * 1e8))); // Default formatting
|
||||
}
|
||||
priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' ');
|
||||
|
||||
// Pad the string with spaces if necessary
|
||||
if (priceString.length() < NUM_SCREENS)
|
||||
{
|
||||
priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' ');
|
||||
}
|
||||
|
||||
if (currencySymbol != CURRENCY_USD || price >= 100000000) // no time anymore when earlier than 1
|
||||
if (currencySymbol != CURRENCY_USD)
|
||||
ret[0] = "SATS/" + getCurrencyCode(currencySymbol);
|
||||
else
|
||||
ret[0] = "MSCW/TIME";
|
||||
|
@ -276,7 +220,7 @@ std::array<std::string, NUM_SCREENS> parseMarketCap(std::uint32_t blockHeight, s
|
|||
std::array<std::string, NUM_SCREENS> ret;
|
||||
std::uint32_t firstIndex = 0;
|
||||
double supply = getSupplyAtBlock(blockHeight);
|
||||
uint64_t marketCap = static_cast<std::uint64_t>(supply * double(price));
|
||||
int64_t marketCap = static_cast<std::int64_t>(supply * double(price));
|
||||
|
||||
ret[0] = getCurrencyCode(currencySymbol) + "/MCAP";
|
||||
|
||||
|
@ -312,7 +256,7 @@ std::array<std::string, NUM_SCREENS> parseMarketCap(std::uint32_t blockHeight, s
|
|||
ret[i] = "";
|
||||
}
|
||||
|
||||
ret[NUM_SCREENS - groups - 1] = std::string(" ") + currencySymbol + " ";
|
||||
ret[NUM_SCREENS - groups - 1] = " $ ";
|
||||
for (std::uint32_t i = 0; i < groups; i++)
|
||||
{
|
||||
ret[(NUM_SCREENS - groups + i)] = stringValue.substr(i * 3, 3).c_str();
|
||||
|
@ -348,9 +292,9 @@ emscripten::val parseBlockHeightArray(std::uint32_t blockHeight)
|
|||
return arrayToStringArray(parseBlockHeight(blockHeight));
|
||||
}
|
||||
|
||||
emscripten::val parsePriceDataArray(std::uint32_t price, const std::string ¤cySymbol, bool useSuffixFormat = false, bool mowMode = false, bool shareDot = false)
|
||||
emscripten::val parsePriceDataArray(std::uint32_t price, const std::string ¤cySymbol, bool useSuffixFormat = false)
|
||||
{
|
||||
return arrayToStringArray(parsePriceData(price, currencySymbol[0], useSuffixFormat, mowMode, shareDot));
|
||||
return arrayToStringArray(parsePriceData(price, currencySymbol[0], useSuffixFormat));
|
||||
}
|
||||
|
||||
emscripten::val parseHalvingCountdownArray(std::uint32_t blockHeight, bool asBlocks)
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#include <string>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
|
@ -20,7 +19,7 @@ const std::string CURRENCY_CODE_JPY = "JPY";
|
|||
const std::string CURRENCY_CODE_AUD = "AUD";
|
||||
const std::string CURRENCY_CODE_CAD = "CAD";
|
||||
|
||||
std::array<std::string, NUM_SCREENS> parsePriceData(std::uint32_t price, char currency, bool useSuffixFormat = false, bool mowMode = false, bool shareDot = false);
|
||||
std::array<std::string, NUM_SCREENS> parsePriceData(std::uint32_t price, char currency, bool useSuffixFormat = false);
|
||||
std::array<std::string, NUM_SCREENS> parseSatsPerCurrency(std::uint32_t price, char currencySymbol, bool withSatsSymbol);
|
||||
std::array<std::string, NUM_SCREENS> parseBlockHeight(std::uint32_t blockHeight);
|
||||
std::array<std::string, NUM_SCREENS> parseHalvingCountdown(std::uint32_t blockHeight, bool asBlocks);
|
||||
|
|
|
@ -28,12 +28,7 @@ double getSupplyAtBlock(std::uint32_t blockNr)
|
|||
return totalBitcoinInCirculation;
|
||||
}
|
||||
|
||||
std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters)
|
||||
{
|
||||
return formatNumberWithSuffix(num, numCharacters, false);
|
||||
}
|
||||
|
||||
std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters, bool mowMode)
|
||||
std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters)
|
||||
{
|
||||
static char result[20]; // Adjust size as needed
|
||||
const long long quadrillion = 1000000000000000LL;
|
||||
|
@ -61,53 +56,29 @@ std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters, bool mo
|
|||
numDouble /= billion;
|
||||
suffix = 'B';
|
||||
}
|
||||
else if (num >= million || numDigits > 6 || (mowMode && num >= thousand))
|
||||
else if (num >= million || numDigits > 6)
|
||||
{
|
||||
numDouble /= million;
|
||||
suffix = 'M';
|
||||
}
|
||||
else if (!mowMode && (num >= thousand || numDigits > 3))
|
||||
else if (num >= thousand || numDigits > 3)
|
||||
{
|
||||
numDouble /= thousand;
|
||||
suffix = 'K';
|
||||
}
|
||||
else if (!mowMode)
|
||||
else
|
||||
{
|
||||
snprintf(result, sizeof(result), "%llu", (unsigned long long)num);
|
||||
sprintf(result, "%llu", (unsigned long long)num);
|
||||
return result;
|
||||
}
|
||||
else // mowMode is true and num < 1000
|
||||
{
|
||||
numDouble /= million;
|
||||
suffix = 'M';
|
||||
}
|
||||
|
||||
// Add suffix
|
||||
int len;
|
||||
int len = snprintf(result, sizeof(result), "%.0f%c", numDouble, suffix);
|
||||
|
||||
// Mow Mode always uses string truncation to avoid rounding
|
||||
std::string mowAsString = std::to_string(numDouble);
|
||||
if (mowMode) {
|
||||
// Default to one decimal place
|
||||
len = snprintf(result, sizeof(result), "%s%c", mowAsString.substr(0, mowAsString.find(".") + 2).c_str(), suffix);
|
||||
}
|
||||
else
|
||||
{
|
||||
len = snprintf(result, sizeof(result), "%.0f%c", numDouble, suffix);
|
||||
}
|
||||
|
||||
// If there's room, add more decimal places
|
||||
// If there's room, add decimal places
|
||||
if (len < numCharacters)
|
||||
{
|
||||
int restLen = mowMode ? numCharacters - len : numCharacters - len - 1;
|
||||
|
||||
if (mowMode) {
|
||||
snprintf(result, sizeof(result), "%s%c", mowAsString.substr(0, mowAsString.find(".") + 2 + restLen).c_str(), suffix);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(result, sizeof(result), "%.*f%c", restLen, numDouble, suffix);
|
||||
}
|
||||
snprintf(result, sizeof(result), "%.*f%c", numCharacters - len - 1, numDouble, suffix);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -164,82 +135,3 @@ int64_t getAmountInSatoshis(std::string bolt11) {
|
|||
|
||||
return satoshis;
|
||||
}
|
||||
|
||||
void parseHashrateString(const std::string& hashrate, std::string& label, std::string& output, unsigned int maxCharacters) {
|
||||
// Handle empty string or "0" cases
|
||||
if (hashrate.empty() || hashrate == "0") {
|
||||
label = "H/S";
|
||||
output = "0";
|
||||
return;
|
||||
}
|
||||
|
||||
size_t suffixLength = 0;
|
||||
if (hashrate.length() > 21) {
|
||||
label = "ZH/S";
|
||||
suffixLength = 21;
|
||||
} else if (hashrate.length() > 18) {
|
||||
label = "EH/S";
|
||||
suffixLength = 18;
|
||||
} else if (hashrate.length() > 15) {
|
||||
label = "PH/S";
|
||||
suffixLength = 15;
|
||||
} else if (hashrate.length() > 12) {
|
||||
label = "TH/S";
|
||||
suffixLength = 12;
|
||||
} else if (hashrate.length() > 9) {
|
||||
label = "GH/S";
|
||||
suffixLength = 9;
|
||||
} else if (hashrate.length() > 6) {
|
||||
label = "MH/S";
|
||||
suffixLength = 6;
|
||||
} else if (hashrate.length() > 3) {
|
||||
label = "KH/S";
|
||||
suffixLength = 3;
|
||||
} else {
|
||||
label = "H/S";
|
||||
suffixLength = 0;
|
||||
}
|
||||
|
||||
double value = std::stod(hashrate) / std::pow(10, suffixLength);
|
||||
|
||||
// Calculate integer part length
|
||||
int integerPartLength = std::to_string(static_cast<int>(value)).length();
|
||||
|
||||
// Calculate remaining space for decimals
|
||||
int remainingSpace = maxCharacters - integerPartLength;
|
||||
|
||||
char buffer[32];
|
||||
if (remainingSpace <= 0)
|
||||
{
|
||||
// No space for decimals, just round to integer
|
||||
snprintf(buffer, sizeof(buffer), "%.0f", value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Space for decimal point and some decimals
|
||||
snprintf(buffer, sizeof(buffer), "%.*f", remainingSpace - 1, value);
|
||||
}
|
||||
|
||||
// Remove trailing zeros and decimal point if necessary
|
||||
output = buffer;
|
||||
if (output.find('.') != std::string::npos)
|
||||
{
|
||||
output = output.substr(0, output.find_last_not_of('0') + 1);
|
||||
if (output.back() == '.')
|
||||
{
|
||||
output.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int getHashrateMultiplier(char unit) {
|
||||
if (unit == '0')
|
||||
return 0;
|
||||
|
||||
static const std::unordered_map<char, int> multipliers = {
|
||||
{'Z', 21}, {'E', 18}, {'P', 15}, {'T', 12},
|
||||
{'G', 9}, {'M', 6}, {'K', 3}
|
||||
};
|
||||
return multipliers.at(unit);
|
||||
}
|
||||
|
|
|
@ -5,15 +5,10 @@
|
|||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
int modulo(int x,int N);
|
||||
|
||||
double getSupplyAtBlock(std::uint32_t blockNr);
|
||||
|
||||
std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters = 4);
|
||||
std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters, bool mowMode);
|
||||
int64_t getAmountInSatoshis(std::string bolt11);
|
||||
void parseHashrateString(const std::string& hashrate, std::string& label, std::string& output, unsigned int maxCharacters);
|
||||
int getHashrateMultiplier(char unit);
|
||||
int64_t getAmountInSatoshis(std::string bolt11);
|
|
@ -1,20 +0,0 @@
|
|||
identifier: BTClock
|
||||
maintainers:
|
||||
- npub1k5f85zx0xdskyayqpfpc0zq6n7vwqjuuxugkayk72fgynp34cs3qfcvqg2
|
||||
relays:
|
||||
- wss://relay.noderunners.network/
|
||||
- wss://nostr.sathoarder.com/
|
||||
- wss://offchain.pub/
|
||||
- wss://nostr3.daedaluslabs.io/
|
||||
- wss://nostr4.daedaluslabs.io/
|
||||
- wss://nostr.dbtc.link/
|
||||
- wss://purplepag.es/
|
||||
- wss://nos.lol/
|
||||
- wss://nostr1.daedaluslabs.io/
|
||||
- wss://nostr.noderunners.network/
|
||||
- wss://nostr.lnbitcoin.cz/
|
||||
- wss://relay.primal.net/
|
||||
- wss://relay.damus.io
|
||||
- wss://nostr-relay.derekross.me/
|
||||
- wss://nostr2.azzamo.net/
|
||||
- wss://nostr2.daedaluslabs.io/
|
|
@ -1,7 +1,7 @@
|
|||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x1b8000,
|
||||
app1, app, ota_1, , 0x1b8000,
|
||||
spiffs, data, spiffs, , 0x66C00,
|
||||
coredump, data, coredump,, 0x10000,
|
||||
nvs, data, nvs, 36K, 20K,
|
||||
otadata, data, ota, 56K, 8K,
|
||||
app0, app, ota_0, 64K, 1700K,
|
||||
app1, app, ota_1, , 1700K,
|
||||
spiffs, data, spiffs, , 400K,
|
||||
coredump, data, coredump,, 64K,
|
||||
|
|
|
|
@ -1,7 +1,7 @@
|
|||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x6F0000,
|
||||
app1, app, ota_1, , 0x6F0000,
|
||||
spiffs, data, spiffs, , 0x200000,
|
||||
coredump, data, coredump,, 0x10000,
|
||||
nvs, data, nvs, 36K, 20K,
|
||||
otadata, data, ota, 56K, 8K,
|
||||
app0, app, ota_0, 64K, 4096K,
|
||||
app1, app, ota_1, , 4096K,
|
||||
spiffs, data, spiffs, , 3072K,
|
||||
coredump, data, coredump,, 64K,
|
||||
|
|
|
|
@ -1,7 +1,7 @@
|
|||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x370000,
|
||||
app1, app, ota_1, , 0x370000,
|
||||
spiffs, data, spiffs, , 0xCD000,
|
||||
coredump, data, coredump,, 0x10000,
|
||||
nvs, data, nvs, 36K, 20K,
|
||||
otadata, data, ota, 56K, 8K,
|
||||
app0, app, ota_0, 64K, 1700K,
|
||||
app1, app, ota_1, , 1700K,
|
||||
spiffs, data, spiffs, , 400K,
|
||||
coredump, data, coredump,, 64K,
|
||||
|
|
|
240
platformio.ini
240
platformio.ini
|
@ -7,190 +7,150 @@
|
|||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
data_dir = data/build_gz
|
||||
default_envs = lolin_s3_mini_213epd, lolin_s3_mini_29epd, btclock_rev_b_213epd, btclock_v8_213epd
|
||||
default_envs = lolin_s3_mini_213epd, lolin_s3_mini_29epd, btclock_rev_b_213epd
|
||||
|
||||
[env]
|
||||
|
||||
|
||||
[btclock_base]
|
||||
platform = espressif32 @ ^6.9.0
|
||||
framework = arduino, espidf
|
||||
platform = espressif32 @ ^6.6.0
|
||||
framework = arduino, espidf
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_exception_decoder, colorize
|
||||
board_build.filesystem = littlefs
|
||||
extra_scripts = pre:scripts/pre_script.py, post:scripts/extra_script.py
|
||||
platform_packages =
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower
|
||||
board_build.embed_files =
|
||||
x509_crt_bundle
|
||||
build_flags =
|
||||
!python scripts/git_rev.py
|
||||
-DLAST_BUILD_TIME=$UNIX_TIME
|
||||
-DARDUINO_USB_CDC_ON_BOOT
|
||||
-DCORE_DEBUG_LEVEL=0
|
||||
-fexceptions
|
||||
extra_scripts = post:scripts/extra_script.py
|
||||
build_flags =
|
||||
!python scripts/git_rev.py
|
||||
-DLAST_BUILD_TIME=$UNIX_TIME
|
||||
-DARDUINO_USB_CDC_ON_BOOT
|
||||
-DCORE_DEBUG_LEVEL=0
|
||||
-fexceptions
|
||||
build_unflags =
|
||||
-Werror=all
|
||||
-fno-exceptions
|
||||
-Werror=all
|
||||
-fno-exceptions
|
||||
lib_deps =
|
||||
https://github.com/joltwallet/esp_littlefs.git
|
||||
bblanchon/ArduinoJson@^7.2.1
|
||||
mathieucarbou/ESPAsyncWebServer @ 3.3.23
|
||||
robtillaart/MCP23017@^0.8.0
|
||||
adafruit/Adafruit NeoPixel@^1.12.3
|
||||
https://github.com/dsbaars/universal_pin#feature/mcp23017_rt
|
||||
https://github.com/dsbaars/GxEPD2#universal_pin
|
||||
https://github.com/tzapu/WiFiManager.git#v2.0.17
|
||||
rblb/Nostrduino@1.2.8
|
||||
|
||||
https://github.com/joltwallet/esp_littlefs.git
|
||||
bblanchon/ArduinoJson@^7.1.0
|
||||
mathieucarbou/ESPAsyncWebServer @ 3.2.0
|
||||
adafruit/Adafruit BusIO@^1.16.1
|
||||
adafruit/Adafruit MCP23017 Arduino Library@^2.3.2
|
||||
adafruit/Adafruit NeoPixel@^1.12.3
|
||||
https://github.com/dsbaars/universal_pin
|
||||
https://github.com/dsbaars/GxEPD2#universal_pin
|
||||
https://github.com/tzapu/WiFiManager.git#v2.0.17
|
||||
rblb/Nostrduino@1.2.8
|
||||
|
||||
[env:lolin_s3_mini]
|
||||
extends = btclock_base
|
||||
board = lolin_s3_mini
|
||||
board_build.partitions = partition.csv
|
||||
build_flags =
|
||||
${btclock_base.build_flags}
|
||||
-D MCP_INT_PIN=8
|
||||
-D NEOPIXEL_PIN=34
|
||||
-D NEOPIXEL_COUNT=4
|
||||
-D NUM_SCREENS=7
|
||||
-D I2C_SDA_PIN=35
|
||||
-D I2C_SCK_PIN=36
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-D IS_HW_REV_A
|
||||
build_flags =
|
||||
${btclock_base.build_flags}
|
||||
-D MCP_INT_PIN=8
|
||||
-D NEOPIXEL_PIN=34
|
||||
-D NEOPIXEL_COUNT=4
|
||||
-D NUM_SCREENS=7
|
||||
-D I2C_SDA_PIN=35
|
||||
-D I2C_SCK_PIN=36
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-D IS_HW_REV_A
|
||||
build_unflags =
|
||||
${btclock_base.build_unflags}
|
||||
platform_packages =
|
||||
platformio/tool-mklittlefs@^1.203.210628
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||
${btclock_base.build_unflags}
|
||||
|
||||
|
||||
[env:btclock_rev_b]
|
||||
extends = btclock_base
|
||||
board = btclock_rev_b
|
||||
board_build.partitions = partition_8mb.csv
|
||||
build_flags =
|
||||
${btclock_base.build_flags}
|
||||
-D MCP_INT_PIN=8
|
||||
-D NEOPIXEL_PIN=15
|
||||
-D NEOPIXEL_COUNT=4
|
||||
-D NUM_SCREENS=7
|
||||
-D I2C_SDA_PIN=35
|
||||
-D I2C_SCK_PIN=36
|
||||
-D HAS_FRONTLIGHT
|
||||
-D PCA_OE_PIN=45
|
||||
-D PCA_I2C_ADDR=0x42
|
||||
-D IS_HW_REV_B
|
||||
board_build.partitions = partition.csv
|
||||
build_flags =
|
||||
${btclock_base.build_flags}
|
||||
-D MCP_INT_PIN=8
|
||||
-D NEOPIXEL_PIN=15
|
||||
-D NEOPIXEL_COUNT=4
|
||||
-D NUM_SCREENS=7
|
||||
-D I2C_SDA_PIN=35
|
||||
-D I2C_SCK_PIN=36
|
||||
-D HAS_FRONTLIGHT
|
||||
-D PCA_OE_PIN=45
|
||||
-D PCA_I2C_ADDR=0x42
|
||||
-D IS_HW_REV_B
|
||||
lib_deps =
|
||||
${btclock_base.lib_deps}
|
||||
robtillaart/PCA9685@^0.7.1
|
||||
claws/BH1750@^1.3.0
|
||||
${btclock_base.lib_deps}
|
||||
robtillaart/PCA9685@^0.7.1
|
||||
claws/BH1750@^1.3.0
|
||||
build_unflags =
|
||||
${btclock_base.build_unflags}
|
||||
platform_packages =
|
||||
platformio/tool-mklittlefs@^1.203.210628
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||
${btclock_base.build_unflags}
|
||||
|
||||
[env:lolin_s3_mini_213epd]
|
||||
extends = env:lolin_s3_mini
|
||||
test_framework = unity
|
||||
build_flags =
|
||||
${env:lolin_s3_mini.build_flags}
|
||||
-D USE_QR
|
||||
-D VERSION_EPD_2_13
|
||||
-D HW_REV=\"REV_A_EPD_2_13\"
|
||||
platform_packages =
|
||||
platformio/tool-mklittlefs@^1.203.210628
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||
build_flags =
|
||||
${env:lolin_s3_mini.build_flags}
|
||||
-D USE_QR
|
||||
-D VERSION_EPD_2_13
|
||||
-D HW_REV=\"REV_A_EPD_2_13\"
|
||||
|
||||
|
||||
[env:btclock_rev_b_213epd]
|
||||
extends = env:btclock_rev_b
|
||||
test_framework = unity
|
||||
build_flags =
|
||||
${env:btclock_rev_b.build_flags}
|
||||
-D USE_QR
|
||||
-D VERSION_EPD_2_13
|
||||
-D HW_REV=\"REV_B_EPD_2_13\"
|
||||
platform_packages =
|
||||
platformio/tool-mklittlefs@^1.203.210628
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||
build_flags =
|
||||
${env:btclock_rev_b.build_flags}
|
||||
-D USE_QR
|
||||
-D VERSION_EPD_2_13
|
||||
-D HW_REV=\"REV_B_EPD_2_13\"
|
||||
|
||||
[env:lolin_s3_mini_29epd]
|
||||
extends = env:lolin_s3_mini
|
||||
test_framework = unity
|
||||
build_flags =
|
||||
${env:lolin_s3_mini.build_flags}
|
||||
-D USE_QR
|
||||
-D VERSION_EPD_2_9
|
||||
-D HW_REV=\"REV_A_EPD_2_9\"
|
||||
platform_packages =
|
||||
platformio/tool-mklittlefs@^1.203.210628
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||
build_flags =
|
||||
${env:lolin_s3_mini.build_flags}
|
||||
-D USE_QR
|
||||
-D VERSION_EPD_2_9
|
||||
-D HW_REV=\"REV_A_EPD_2_9\"
|
||||
|
||||
[env:btclock_rev_b_29epd]
|
||||
extends = env:btclock_rev_b
|
||||
test_framework = unity
|
||||
build_flags =
|
||||
${env:btclock_rev_b.build_flags}
|
||||
-D USE_QR
|
||||
-D VERSION_EPD_2_9
|
||||
-D HW_REV=\"REV_B_EPD_2_9\"
|
||||
platform_packages =
|
||||
platformio/tool-mklittlefs@^1.203.210628
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||
build_flags =
|
||||
${env:btclock_rev_b.build_flags}
|
||||
-D USE_QR
|
||||
-D VERSION_EPD_2_9
|
||||
-D HW_REV=\"REV_B_EPD_2_9\"
|
||||
|
||||
[env:btclock_v8]
|
||||
[env:btclock_s3]
|
||||
extends = btclock_base
|
||||
board = btclock_v8
|
||||
board = btclock
|
||||
board_build.partitions = partition_16mb.csv
|
||||
board_build.flash_mode = qio
|
||||
test_framework = unity
|
||||
build_flags =
|
||||
${btclock_base.build_flags}
|
||||
-D MCP_INT_PIN=4
|
||||
-D NEOPIXEL_PIN=5
|
||||
-D NEOPIXEL_COUNT=4
|
||||
-D NUM_SCREENS=8
|
||||
-D SPI_SDA_PIN=11
|
||||
-D SPI_SCK_PIN=12
|
||||
-D I2C_SDA_PIN=1
|
||||
-D I2C_SCK_PIN=2
|
||||
-D MCP_RESET_PIN=21
|
||||
-D MCP1_A0_PIN=6
|
||||
-D MCP1_A1_PIN=7
|
||||
-D MCP1_A2_PIN=8
|
||||
-D MCP2_A0_PIN=9
|
||||
-D MCP2_A1_PIN=10
|
||||
-D MCP2_A2_PIN=14
|
||||
build_flags =
|
||||
${btclock_base.build_flags}
|
||||
-D MCP_INT_PIN=4
|
||||
-D NEOPIXEL_PIN=5
|
||||
-D NEOPIXEL_COUNT=4
|
||||
-D NUM_SCREENS=8
|
||||
-D SPI_SDA_PIN=11
|
||||
-D SPI_SCK_PIN=12
|
||||
-D I2C_SDA_PIN=1
|
||||
-D I2C_SCK_PIN=2
|
||||
-D MCP_RESET_PIN=21
|
||||
-D MCP1_A0_PIN=6
|
||||
-D MCP1_A1_PIN=7
|
||||
-D MCP1_A2_PIN=8
|
||||
-D MCP2_A0_PIN=9
|
||||
-D MCP2_A1_PIN=10
|
||||
-D MCP2_A2_PIN=14
|
||||
build_unflags =
|
||||
${btclock_base.build_unflags}
|
||||
platform_packages =
|
||||
platformio/tool-mklittlefs@^1.203.210628
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||
|
||||
[env:btclock_v8_213epd]
|
||||
extends = env:btclock_v8
|
||||
test_framework = unity
|
||||
build_flags =
|
||||
${env:btclock_v8.build_flags}
|
||||
-D USE_QR
|
||||
-D VERSION_EPD_2_13
|
||||
-D HW_REV=\"REV_V8_EPD_2_13\"
|
||||
platform_packages =
|
||||
platformio/tool-mklittlefs@^1.203.210628
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||
${btclock_base.build_unflags}
|
||||
|
||||
[env:native_test_only]
|
||||
platform = native
|
||||
test_framework = unity
|
||||
build_flags =
|
||||
${btclock_base.build_flags}
|
||||
-D MCP_INT_PIN=8
|
||||
-D NEOPIXEL_PIN=34
|
||||
-D NEOPIXEL_COUNT=4
|
||||
-D NUM_SCREENS=7
|
||||
-D UNITY_TEST
|
||||
-std=gnu++17
|
||||
platform_packages =
|
||||
platformio/tool-mklittlefs@^1.203.210628
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||
build_flags =
|
||||
${btclock_base.build_flags}
|
||||
-D MCP_INT_PIN=8
|
||||
-D NEOPIXEL_PIN=34
|
||||
-D NEOPIXEL_COUNT=4
|
||||
-D NUM_SCREENS=7
|
|
@ -1 +0,0 @@
|
|||
platformio
|
|
@ -1,13 +1,10 @@
|
|||
Import("env")
|
||||
import os
|
||||
import gzip
|
||||
from shutil import copyfileobj, rmtree, copyfile, copytree
|
||||
from shutil import copyfileobj, rmtree
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
|
||||
|
||||
|
||||
revision = (
|
||||
subprocess.check_output(["git", "rev-parse", "HEAD"])
|
||||
.strip()
|
||||
|
@ -29,7 +26,7 @@ def process_directory(input_dir, output_dir):
|
|||
Path(output_root).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for file in files:
|
||||
# if not file.endswith(('.bin')):
|
||||
# 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)
|
||||
|
@ -41,85 +38,10 @@ def process_directory(input_dir, output_dir):
|
|||
|
||||
# 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'
|
||||
# copytree("assets", "data/dist/assets")
|
||||
|
||||
process_directory(input_directory, output_directory)
|
||||
|
||||
def get_fs_partition_size(env):
|
||||
import csv
|
||||
|
||||
# Get partition table path - first try custom, then default
|
||||
board_config = env.BoardConfig()
|
||||
partition_table = board_config.get("build.partitions", "default.csv")
|
||||
|
||||
# Handle default partition table path
|
||||
if partition_table == "default.csv" or partition_table == "huge_app.csv":
|
||||
partition_table = os.path.join(env.PioPlatform().get_package_dir("framework-arduinoespressif32"),
|
||||
"tools", "partitions", partition_table)
|
||||
|
||||
# Parse CSV to find spiffs/littlefs partition
|
||||
with open(partition_table, 'r') as f:
|
||||
for row in csv.reader(f):
|
||||
if len(row) < 5:
|
||||
continue
|
||||
# Remove comments and whitespace
|
||||
row = [cell.strip().split('#')[0] for cell in row]
|
||||
# Check if this is a spiffs or littlefs partition
|
||||
if row[0].startswith(('spiffs', 'littlefs')):
|
||||
# Size is in hex format
|
||||
return int(row[4], 16)
|
||||
return 0
|
||||
|
||||
def get_littlefs_used_size(binary_path):
|
||||
mklittlefs_path = os.path.join(env.PioPlatform().get_package_dir("tool-mklittlefs-rp2040-earlephilhower"), "mklittlefs")
|
||||
|
||||
try:
|
||||
result = subprocess.run([mklittlefs_path, '-l', binary_path], capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
# Parse the output to sum up file sizes
|
||||
total_size = 0
|
||||
for line in result.stdout.splitlines():
|
||||
if line.strip() and not line.startswith('<dir>') and not line.startswith('Creation'):
|
||||
# Each line format: size filename
|
||||
size = line.split()[0]
|
||||
total_size += int(size)
|
||||
return total_size
|
||||
except Exception as e:
|
||||
print(f"Error getting filesystem size: {e}")
|
||||
return 0
|
||||
|
||||
|
||||
def after_littlefs(source, target, env):
|
||||
binary_path = str(target[0])
|
||||
partition_size = get_fs_partition_size(env)
|
||||
used_size = get_littlefs_used_size(binary_path)
|
||||
|
||||
percentage = (used_size / partition_size) * 100
|
||||
bar_width = 50
|
||||
filled = int(bar_width * percentage / 100)
|
||||
bar = '=' * filled + '-' * (bar_width - filled)
|
||||
|
||||
print(f"\nLittleFS Actual Usage: [{bar}] {percentage:.1f}% ({used_size}/{partition_size} bytes)")
|
||||
|
||||
|
||||
flash_size = env.BoardConfig().get("upload.flash_size", "4MB")
|
||||
fs_image_name = f"littlefs_{flash_size}"
|
||||
env.Replace(ESP32_FS_IMAGE_NAME=fs_image_name)
|
||||
env.Replace(ESP8266_FS_IMAGE_NAME=fs_image_name)
|
||||
|
||||
os.environ["PUBLIC_BASE_URL"] = ""
|
||||
fs_name = env.get("ESP32_FS_IMAGE_NAME", "littlefs.bin")
|
||||
# Or alternatively:
|
||||
# fs_name = env.get("FSTOOLNAME", "littlefs.bin")
|
||||
|
||||
# Use the variable in the pre-action
|
||||
env.AddPreAction(f"$BUILD_DIR/{fs_name}.bin", before_buildfs)
|
||||
env.AddPostAction(f"$BUILD_DIR/{fs_name}.bin", after_littlefs)
|
||||
# LittleFS Actual Usage: [==============================--------------------] 60.4% (254165/420864 bytes)
|
||||
# LittleFS Actual Usage: [==============================--------------------] 60.2% (253476/420864 bytes)
|
||||
# 372736 used
|
||||
env.AddPreAction("$BUILD_DIR/littlefs.bin", before_buildfs)
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
Import("env")
|
||||
|
||||
flash_size = env.BoardConfig().get("upload.flash_size", "4MB")
|
||||
fs_image_name = f"littlefs_{flash_size}"
|
||||
env.Replace(ESP32_FS_IMAGE_NAME=fs_image_name)
|
||||
env.Replace(ESP8266_FS_IMAGE_NAME=fs_image_name)
|
||||
|
|
@ -7,8 +7,8 @@ CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y
|
|||
#CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
||||
#CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
|
||||
#CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=n
|
||||
#CONFIG_ESP_TLS_INSECURE=y
|
||||
#CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
|
||||
CONFIG_ESP_TLS_INSECURE=y
|
||||
CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
|
||||
|
||||
CONFIG_HEAP_CORRUPTION_DETECTION=CONFIG_HEAP_POISONING_LIGHT
|
||||
CONFIG_HEAP_POISONING_LIGHT=y
|
||||
|
@ -16,14 +16,18 @@ CONFIG_HEAP_POISONING_LIGHT=y
|
|||
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
|
||||
CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL=0
|
||||
CONFIG_LOG_BOOTLOADER_LEVEL_NONE=y
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y
|
||||
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL_NONE=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL=0
|
||||
CONFIG_LOG_MAXIMUM_LEVEL=0
|
||||
CONFIG_LOG_BOOTLOADER_LEVEL_NONE=y
|
||||
CONFIG_LOG_BOOTLOADER_LEVEL=0
|
||||
CONFIG_CXX_EXCEPTIONS=y
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
#CONFIG_BOOTLOADER_WDT_ENABLE=n
|
||||
#CONFIG_ESP_TASK_WDT=n
|
||||
#CONFIG_TASK_WDT=n
|
||||
|
||||
#Required for BTClock
|
||||
#CONFIG_SPIRAM_MODE_OCT=y
|
||||
|
@ -38,11 +42,12 @@ CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=12
|
|||
CONFIG_ESP32_WIFI_RX_BA_WIN=6
|
||||
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3120
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_RTC_CLK_CAL_CYCLES=576
|
||||
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3120
|
||||
CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n
|
||||
CONFIG_SPIRAM_CACHE_WORKAROUND=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
#CONFIG_NEWLIB_NANO_FORMAT=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
|
|
@ -1526,78 +1526,7 @@ const uint8_t Antonio_SemiBold40pt7bBitmaps[] PROGMEM = {
|
|||
0x3F, 0xE0, 0x3F, 0xE0, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0x80, 0xFF, 0xFF,
|
||||
0xFE, 0x00, 0xFF, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF,
|
||||
0xC0, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xF8, 0x00, 0x00, 0x1F, 0x80,
|
||||
0x00,
|
||||
// euro
|
||||
0x00, 0x07, 0xFC, 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0x00,
|
||||
0x07, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0xFF, 0x00,
|
||||
0xFF, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xFE, 0x07, 0xFE, 0x07, 0xFC, 0x0F,
|
||||
0xF8, 0x0F, 0xF8, 0x1F, 0xF0, 0x0F, 0xF8, 0x3F, 0xE0, 0x1F, 0xF0, 0xFF,
|
||||
0x80, 0x3F, 0xE1, 0xFF, 0x00, 0x7F, 0xC3, 0xFE, 0x00, 0xFF, 0x87, 0xFC,
|
||||
0x00, 0xFF, 0x0F, 0xF8, 0x01, 0xFE, 0x1F, 0xF0, 0x03, 0xFC, 0x3F, 0xE0,
|
||||
0x07, 0xF8, 0x7F, 0xC0, 0x0F, 0xF0, 0xFF, 0x80, 0x1F, 0xE1, 0xFF, 0x00,
|
||||
0x3F, 0xC3, 0xFE, 0x00, 0x7F, 0x87, 0xFC, 0x00, 0xFF, 0x0F, 0xF8, 0x01,
|
||||
0xFE, 0x1F, 0xF0, 0x03, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0x80,
|
||||
0x0F, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xFC, 0x00,
|
||||
0x07, 0xFC, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00,
|
||||
0x3F, 0xE0, 0x00, 0x07, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFF, 0x00, 0x1F,
|
||||
0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0x0F,
|
||||
0xF8, 0x00, 0x00, 0x1F, 0xF0, 0x03, 0xFC, 0x3F, 0xE0, 0x07, 0xF8, 0x7F,
|
||||
0xC0, 0x0F, 0xF0, 0xFF, 0x80, 0x1F, 0xE1, 0xFF, 0x00, 0x3F, 0xC3, 0xFE,
|
||||
0x00, 0x7F, 0x87, 0xFC, 0x00, 0xFF, 0x0F, 0xF8, 0x01, 0xFE, 0x1F, 0xF0,
|
||||
0x03, 0xFC, 0x3F, 0xE0, 0x07, 0xF8, 0x7F, 0xC0, 0x0F, 0xF0, 0xFF, 0x80,
|
||||
0x1F, 0xE1, 0xFF, 0x00, 0x7F, 0xC3, 0xFE, 0x00, 0xFF, 0x87, 0xFC, 0x01,
|
||||
0xFF, 0x0F, 0xF8, 0x03, 0xFE, 0x0F, 0xF8, 0x07, 0xFC, 0x1F, 0xF0, 0x0F,
|
||||
0xF0, 0x3F, 0xE0, 0x3F, 0xE0, 0x3F, 0xE0, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF,
|
||||
0x80, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xF0,
|
||||
0x00, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xF8, 0x00,
|
||||
0x00, 0x3F, 0x80, 0x00,
|
||||
// pound
|
||||
0x00, 0x0F, 0xFC, 0x00, 0x01, 0xFF, 0xFC, 0x00, 0x0F, 0xFF, 0xFC, 0x00,
|
||||
0x7F, 0xFF, 0xF8, 0x03, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xC0, 0x7F,
|
||||
0xFF, 0xFF, 0x83, 0xFF, 0x03, 0xFE, 0x0F, 0xF8, 0x07, 0xF8, 0x3F, 0xC0,
|
||||
0x1F, 0xE1, 0xFF, 0x00, 0x3F, 0xC7, 0xFC, 0x00, 0xFF, 0x1F, 0xE0, 0x03,
|
||||
0xFC, 0x7F, 0x80, 0x0F, 0xF1, 0xFE, 0x00, 0x3F, 0xC7, 0xF8, 0x00, 0xFF,
|
||||
0x1F, 0xF0, 0x03, 0xFC, 0x7F, 0xC0, 0x0F, 0xF1, 0xFF, 0x00, 0x3F, 0xC3,
|
||||
0xFC, 0x00, 0xFF, 0x0F, 0xF0, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0xFF,
|
||||
0x80, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x1F, 0xF0,
|
||||
0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x03, 0xFC, 0x00,
|
||||
0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x3F, 0xFF, 0xFF, 0xF8,
|
||||
0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFE, 0x3F,
|
||||
0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFE, 0x00, 0x00, 0x0F,
|
||||
0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x01, 0xFE,
|
||||
0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x7F, 0xC0,
|
||||
0x00, 0x01, 0xFF, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x1F, 0xF0, 0x00,
|
||||
0x00, 0x7F, 0xC0, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00,
|
||||
0x1F, 0xF0, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x0F,
|
||||
0xF8, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x0F, 0xF8,
|
||||
0x00, 0x00, 0x7F, 0xC0, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x1F, 0xF0, 0x00,
|
||||
0x01, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF,
|
||||
0x7F, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xDF,
|
||||
0xFF, 0xFF, 0xFF,
|
||||
// yen
|
||||
0xFF, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x07, 0xFB, 0xFE, 0x00, 0x1F, 0xF3,
|
||||
0xFC, 0x00, 0x3F, 0xE7, 0xFC, 0x00, 0x7F, 0xCF, 0xF8, 0x00, 0xFF, 0x0F,
|
||||
0xF0, 0x03, 0xFE, 0x1F, 0xE0, 0x07, 0xFC, 0x3F, 0xE0, 0x0F, 0xF0, 0x7F,
|
||||
0xC0, 0x1F, 0xE0, 0x7F, 0x80, 0x7F, 0xC0, 0xFF, 0x80, 0xFF, 0x81, 0xFF,
|
||||
0x01, 0xFE, 0x01, 0xFE, 0x03, 0xFC, 0x03, 0xFC, 0x07, 0xF8, 0x07, 0xFC,
|
||||
0x1F, 0xF0, 0x0F, 0xF8, 0x3F, 0xC0, 0x0F, 0xF0, 0x7F, 0x80, 0x1F, 0xE0,
|
||||
0xFF, 0x00, 0x3F, 0xE3, 0xFC, 0x00, 0x3F, 0xC7, 0xF8, 0x00, 0x7F, 0x8F,
|
||||
0xF0, 0x00, 0xFF, 0x9F, 0xE0, 0x00, 0xFF, 0x7F, 0x80, 0x01, 0xFE, 0xFF,
|
||||
0x00, 0x03, 0xFD, 0xFE, 0x00, 0x07, 0xFF, 0xFC, 0x00, 0x07, 0xFF, 0xF0,
|
||||
0x00, 0x0F, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0x00,
|
||||
0x00, 0x3F, 0xFE, 0x00, 0x00, 0x7F, 0xFC, 0x00, 0x00, 0xFF, 0xF8, 0x00,
|
||||
0x00, 0xFF, 0xE0, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x03, 0xFF, 0x80, 0x07,
|
||||
0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8, 0x3F,
|
||||
0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0xC0, 0x00, 0x00,
|
||||
0xFF, 0x80, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x07,
|
||||
0xFC, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x3F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF,
|
||||
0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF,
|
||||
0xFF, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x0F, 0xF8,
|
||||
0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x7F, 0xC0,
|
||||
0x00, 0x00, 0xFF, 0x80, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x03, 0xFE, 0x00,
|
||||
0x00, 0x07, 0xFC, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x1F, 0xF0, 0x00,
|
||||
0x00, 0x3F, 0xE0, 0x00, 0x00, 0x7F, 0xC0, 0x00
|
||||
};
|
||||
0x00};
|
||||
|
||||
const GFXglyph Antonio_SemiBold40pt7bGlyphs[] PROGMEM = {
|
||||
{0, 1, 1, 17, 0, 0}, // 0x20 ' '
|
||||
|
@ -1659,10 +1588,10 @@ const GFXglyph Antonio_SemiBold40pt7bGlyphs[] PROGMEM = {
|
|||
{10963, 28, 67, 32, 2, -66}, // 0x58 'X'
|
||||
{11198, 31, 67, 32, 1, -66}, // 0x59 'Y'
|
||||
{11458, 23, 67, 27, 3, -66}, // 0x5A 'Z'
|
||||
{18021, 31, 69, 37, 2, -67}, // 0x5B '[' --> euro { 18290, 31, 69, 37, 2, -67 } was {11651, 17, 70, 26, 6, -66}
|
||||
{11800, 24, 67, 30, 3, -66}, // 0x5C '\'
|
||||
{18557, 30, 68, 36, 3, -67 }, // 0x5D ']' --> pound { 0, 30, 68, 36, 3, -67 } was {12001, 16, 70, 26, 4, -66}
|
||||
{18812, 31, 67, 32, 1, -66 }, // 0x5E '^' --> yen { 0, 31, 67, 32, 1, -66 } was {12141, 29, 35, 37, 4, -66
|
||||
{11651, 17, 70, 26, 6, -66}, // 0x5B '['
|
||||
{11800, 24, 67, 30, 3, -66}, // 0x5C '\'
|
||||
{12001, 16, 70, 26, 4, -66}, // 0x5D ']'
|
||||
{12141, 29, 35, 37, 4, -66}, // 0x5E '^'
|
||||
{12268, 25, 7, 29, 2, 2}, // 0x5F '_'
|
||||
{12290, 12, 15, 16, 2, -77}, // 0x60 '`'
|
||||
{12313, 27, 59, 36, 4, -57}, // 0x61 'a'
|
||||
|
@ -1694,13 +1623,10 @@ const GFXglyph Antonio_SemiBold40pt7bGlyphs[] PROGMEM = {
|
|||
{17509, 20, 75, 27, 4, -67}, // 0x7B '{'
|
||||
{17697, 9, 75, 21, 6, -70}, // 0x7C '|'
|
||||
{17782, 19, 75, 27, 4, -67}, // 0x7D '}'
|
||||
{17961, 34, 14, 43, 4, -44}}; // 0x7E '~'
|
||||
|
||||
|
||||
//, {18021, 31, 69, 37, 2, -67}
|
||||
{17961, 34, 14, 43, 4, -44}, {18021, 31, 69, 37, 2, -67}}; // 0x7E '~'
|
||||
|
||||
const GFXfont Antonio_SemiBold40pt7b PROGMEM = {
|
||||
(uint8_t *)Antonio_SemiBold40pt7bBitmaps,
|
||||
(GFXglyph *)Antonio_SemiBold40pt7bGlyphs, 0x20, 0x7E, 100};
|
||||
(GFXglyph *)Antonio_SemiBold40pt7bGlyphs, 0x20, 0x7E, 101};
|
||||
|
||||
// Approx. 18961 bytes
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "antonio-semibold20.h"
|
||||
//#include "antonio-semibold30.h"
|
||||
#include "antonio-semibold30.h"
|
||||
#include "antonio-semibold40.h"
|
||||
#include "antonio-semibold90.h"
|
||||
#include "sats-symbol.h"
|
||||
|
|
1171
src/icons/icons.cpp
1171
src/icons/icons.cpp
File diff suppressed because it is too large
Load diff
|
@ -2,47 +2,54 @@
|
|||
|
||||
char *wsServer;
|
||||
esp_websocket_client_handle_t blockNotifyClient = NULL;
|
||||
uint currentBlockHeight = 873400;
|
||||
uint currentBlockHeight = 840000;
|
||||
uint blockMedianFee = 1;
|
||||
bool blockNotifyInit = false;
|
||||
unsigned long int lastBlockUpdate;
|
||||
|
||||
const char *mempoolWsCert = R"EOF(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
|
||||
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
|
||||
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
|
||||
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
|
||||
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
|
||||
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
|
||||
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
|
||||
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
|
||||
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
|
||||
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
|
||||
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
|
||||
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
|
||||
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
|
||||
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
|
||||
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
|
||||
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
|
||||
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
|
||||
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
|
||||
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
|
||||
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
|
||||
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
|
||||
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
|
||||
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
|
||||
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
|
||||
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
|
||||
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
|
||||
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
|
||||
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
|
||||
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
|
||||
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
|
||||
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
|
||||
jjxDah2nGN59PRbxYvnKkKj9
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
// const char *mempoolWsCert = R"(-----BEGIN CERTIFICATE-----
|
||||
// MIIHfTCCBmWgAwIBAgIRANFX3mhqRYDt1NFuENoSyaAwDQYJKoZIhvcNAQELBQAw
|
||||
// gZUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
|
||||
// BgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDE9MDsGA1UE
|
||||
// AxM0U2VjdGlnbyBSU0EgT3JnYW5pemF0aW9uIFZhbGlkYXRpb24gU2VjdXJlIFNl
|
||||
// cnZlciBDQTAeFw0yMzA3MjQwMDAwMDBaFw0yNDA4MjIyMzU5NTlaMFcxCzAJBgNV
|
||||
// BAYTAkpQMQ4wDAYDVQQIEwVUb2t5bzEgMB4GA1UEChMXTUVNUE9PTCBTUEFDRSBD
|
||||
// Ty4sIExURC4xFjAUBgNVBAMTDW1lbXBvb2wuc3BhY2UwggEiMA0GCSqGSIb3DQEB
|
||||
// AQUAA4IBDwAwggEKAoIBAQCqmiPRWgo58d25R0biQjAksXMq5ciH7z7ZQo2w2AbB
|
||||
// rHxpnlIry74b9S4wRY5UJeYmd6ZwA76NdSioDvxTJc29bLplY+Ftmfc4ET0zYb2k
|
||||
// Fi86z7GOWb6Ezor/qez9uMM9cxd021Bvcs0/2OrL6Sgp66u9keDZv9NyvFPpXfuR
|
||||
// tdV2r4HF57VJqZn105PN4k80kNWgDbae8aw+BuUNvQYKEe71yfB7Bh6zSh9pCSfM
|
||||
// I6pIJdQzoada2uY1dQMoJeIq8qKNKqAPKGsH5McemUT5ZIKU/tjk3nfX0pz/sQa4
|
||||
// CN7tLH6UeUlctei92GFd6Xtn7RbKLhDUbc4Sq02Cc9iXAgMBAAGjggQDMIID/zAf
|
||||
// BgNVHSMEGDAWgBQX2dYlJ2f5McJJQ9kwNkSMbKlP6zAdBgNVHQ4EFgQUXkxoddJ6
|
||||
// rKobsbmDdtuCK1ywXuIwDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAwHQYD
|
||||
// VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEoGA1UdIARDMEEwNQYMKwYBBAGy
|
||||
// MQECAQMEMCUwIwYIKwYBBQUHAgEWF2h0dHBzOi8vc2VjdGlnby5jb20vQ1BTMAgG
|
||||
// BmeBDAECAjBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLnNlY3RpZ28uY29t
|
||||
// L1NlY3RpZ29SU0FPcmdhbml6YXRpb25WYWxpZGF0aW9uU2VjdXJlU2VydmVyQ0Eu
|
||||
// Y3JsMIGKBggrBgEFBQcBAQR+MHwwVQYIKwYBBQUHMAKGSWh0dHA6Ly9jcnQuc2Vj
|
||||
// dGlnby5jb20vU2VjdGlnb1JTQU9yZ2FuaXphdGlvblZhbGlkYXRpb25TZWN1cmVT
|
||||
// ZXJ2ZXJDQS5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29t
|
||||
// MIIBgAYKKwYBBAHWeQIEAgSCAXAEggFsAWoAdwB2/4g/Crb7lVHCYcz1h7o0tKTN
|
||||
// uyncaEIKn+ZnTFo6dAAAAYmc9m/gAAAEAwBIMEYCIQD8XOozx411S/bnZambGjTB
|
||||
// yTcr2fCmggUfQLSmqksD5gIhAIjiEMg0o1VSuQW31gWzfzL6idCkIZeSKN104cdp
|
||||
// xa4SAHcA2ra/az+1tiKfm8K7XGvocJFxbLtRhIU0vaQ9MEjX+6sAAAGJnPZwPwAA
|
||||
// BAMASDBGAiEA2sPTZTzvxewzQ8vk36+BWAKuJS7AvJ5W3clvfwCa8OUCIQC74ekT
|
||||
// Ged2fqQE4sVy74aS6HRA2ihC9VLtNrASJx1YjQB2AO7N0GTV2xrOxVy3nbTNE6Iy
|
||||
// h0Z8vOzew1FIWUZxH7WbAAABiZz2cA8AAAQDAEcwRQIgEklH7wYCFuuJIFUHX5PY
|
||||
// /vZ3bDoxOp+061PT3caa+rICIQC0abgfGlBKiHxp47JZxnW3wcVqWdiYX4ViLm9H
|
||||
// xfx4ljCBxgYDVR0RBIG+MIG7gg1tZW1wb29sLnNwYWNlghMqLmZtdC5tZW1wb29s
|
||||
// LnNwYWNlghMqLmZyYS5tZW1wb29sLnNwYWNlgg8qLm1lbXBvb2wuc3BhY2WCEyou
|
||||
// dGs3Lm1lbXBvb2wuc3BhY2WCEyoudmExLm1lbXBvb2wuc3BhY2WCDGJpc3EubWFy
|
||||
// a2V0c4IKYmlzcS5uaW5qYYIObGlxdWlkLm5ldHdvcmuCDGxpcXVpZC5wbGFjZYIN
|
||||
// bWVtcG9vbC5uaW5qYTANBgkqhkiG9w0BAQsFAAOCAQEAFvOSRnlHDfq9C8acjZEG
|
||||
// 5XIqjNYigyWyjOvx83of6Z3PBKkAZB5D/UHBPp+jBDJiEb/QXC7Z7Y7kpuvnoVib
|
||||
// b4jDc0RjGEsxL+3F7cSw26m3wILJhhHooGZRmFY4GOAeCZtYCOTzJsiZvFpDoQjU
|
||||
// hTBxtaps05z0Ly9/eYvkXnjnBNROZJVR+KYHlq4TIoGNc4q4KvpfHv2I/vhS2M1e
|
||||
// bECNNPEyRxHGKdXXO3huocE7aVKpy+JDR6cWwDu6hpdc1j/SCDqdTDFQ7McHOrqA
|
||||
// fpPh4FcfePMh7Mqxtg2pSs5pXPtiP0ZjLgxd7HbAXct8Y+/jGk+k3sx3SeYXVimr
|
||||
// ew==
|
||||
// -----END CERTIFICATE-----)";
|
||||
|
||||
void setupBlockNotify()
|
||||
{
|
||||
|
@ -96,18 +103,13 @@ void setupBlockNotify()
|
|||
|
||||
esp_websocket_client_config_t config = {
|
||||
// .uri = "wss://mempool.space/api/v1/ws",
|
||||
.task_stack = (6*1024),
|
||||
.user_agent = USER_AGENT
|
||||
// .task_stack = (6*1024),
|
||||
// .cert_pem = mempoolWsCert,
|
||||
.user_agent = USER_AGENT,
|
||||
};
|
||||
|
||||
if (preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE)) {
|
||||
config.cert_pem = mempoolWsCert;
|
||||
}
|
||||
|
||||
config.uri = mempoolUri.c_str();
|
||||
|
||||
Serial.printf("Connecting to %s\r\n", preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE));
|
||||
|
||||
blockNotifyClient = esp_websocket_client_init(&config);
|
||||
esp_websocket_register_events(blockNotifyClient, WEBSOCKET_EVENT_ANY,
|
||||
onWebsocketBlockEvent, blockNotifyClient);
|
||||
|
@ -296,27 +298,42 @@ void restartBlockNotify()
|
|||
}
|
||||
|
||||
|
||||
int getBlockFetch() {
|
||||
try {
|
||||
String mempoolInstance = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
||||
const String protocol = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE) ? "https" : "http";
|
||||
String url = protocol + "://" + mempoolInstance + "/api/blocks/tip/height";
|
||||
int getBlockFetch()
|
||||
{
|
||||
try {
|
||||
WiFiClientSecure client;
|
||||
client.setInsecure();
|
||||
|
||||
HTTPClient* http = HttpHelper::begin(url);
|
||||
Serial.println("Fetching block height from " + url);
|
||||
int httpCode = http->GET();
|
||||
String mempoolInstance =
|
||||
preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
||||
|
||||
if (httpCode > 0 && httpCode == HTTP_CODE_OK) {
|
||||
String blockHeightStr = http->getString();
|
||||
HttpHelper::end(http);
|
||||
return blockHeightStr.toInt();
|
||||
}
|
||||
HttpHelper::end(http);
|
||||
Serial.println("HTTP code" + String(httpCode));
|
||||
} catch (...) {
|
||||
Serial.println(F("An exception occurred while trying to get the latest block"));
|
||||
// Get current block height through regular API
|
||||
HTTPClient http;
|
||||
|
||||
const String protocol = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE) ? "https" : "http";
|
||||
|
||||
if (preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE))
|
||||
http.begin(client, protocol + "://" + mempoolInstance + "/api/blocks/tip/height");
|
||||
else
|
||||
http.begin(protocol + "://" + mempoolInstance + "/api/blocks/tip/height");
|
||||
|
||||
Serial.println("Fetching block height from " + protocol + "://" + mempoolInstance + "/api/blocks/tip/height");
|
||||
int httpCode = http.GET();
|
||||
|
||||
if (httpCode > 0 && httpCode == HTTP_CODE_OK)
|
||||
{
|
||||
String blockHeightStr = http.getString();
|
||||
return blockHeightStr.toInt();
|
||||
} else {
|
||||
Serial.println("HTTP code" + String(httpCode));
|
||||
return 0;
|
||||
}
|
||||
return 2203; // B-T-C
|
||||
}
|
||||
catch (...) {
|
||||
Serial.println(F("An exception occured while trying to get the latest block"));
|
||||
}
|
||||
|
||||
return 2203; // B-T-C
|
||||
}
|
||||
|
||||
uint getLastBlockUpdate()
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include "lib/led_handler.hpp"
|
||||
#include "lib/screen_handler.hpp"
|
||||
#include "lib/timers.hpp"
|
||||
#include "lib/shared.hpp"
|
||||
|
||||
// using namespace websockets;
|
||||
|
|
|
@ -4,52 +4,39 @@ TaskHandle_t buttonTaskHandle = NULL;
|
|||
const TickType_t debounceDelay = pdMS_TO_TICKS(50);
|
||||
TickType_t lastDebounceTime = 0;
|
||||
|
||||
#ifdef IS_BTCLOCK_V8
|
||||
#define BTN_1 256
|
||||
#define BTN_2 512
|
||||
#define BTN_3 1024
|
||||
#define BTN_4 2048
|
||||
#else
|
||||
#define BTN_1 2048
|
||||
#define BTN_2 1024
|
||||
#define BTN_3 512
|
||||
#define BTN_4 256
|
||||
#endif
|
||||
|
||||
void buttonTask(void *parameter) {
|
||||
while (1) {
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
std::lock_guard<std::mutex> lock(mcpMutex);
|
||||
|
||||
TickType_t currentTime = xTaskGetTickCount();
|
||||
|
||||
if ((currentTime - lastDebounceTime) >= debounceDelay) {
|
||||
lastDebounceTime = currentTime;
|
||||
|
||||
if (!digitalRead(MCP_INT_PIN)) {
|
||||
uint pin = mcp1.getInterruptFlagRegister();
|
||||
uint pin = mcp1.getLastInterruptPin();
|
||||
|
||||
switch (pin) {
|
||||
case BTN_1:
|
||||
case 3:
|
||||
toggleTimerActive();
|
||||
break;
|
||||
case BTN_2:
|
||||
case 2:
|
||||
nextScreen();
|
||||
break;
|
||||
case BTN_3:
|
||||
case 1:
|
||||
previousScreen();
|
||||
break;
|
||||
case BTN_4:
|
||||
case 0:
|
||||
showSystemStatusScreen();
|
||||
break;
|
||||
}
|
||||
}
|
||||
mcp1.getInterruptCaptureRegister();
|
||||
mcp1.clearInterrupts();
|
||||
} else {
|
||||
}
|
||||
// Very ugly, but for some reason this is necessary
|
||||
while (!digitalRead(MCP_INT_PIN)) {
|
||||
mcp1.getInterruptCaptureRegister();
|
||||
mcp1.clearInterrupts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include "lib/screen_handler.hpp"
|
||||
#include "lib/shared.hpp"
|
||||
#include "lib/timers.hpp"
|
||||
|
||||
extern TaskHandle_t buttonTaskHandle;
|
||||
|
||||
|
|
|
@ -2,12 +2,10 @@
|
|||
|
||||
#define MAX_ATTEMPTS_WIFI_CONNECTION 20
|
||||
|
||||
// zlib_turbo zt;
|
||||
|
||||
Preferences preferences;
|
||||
MCP23017 mcp1(0x20);
|
||||
#ifdef IS_BTCLOCK_V8
|
||||
MCP23017 mcp2(0x21);
|
||||
Adafruit_MCP23X17 mcp1;
|
||||
#ifdef IS_BTCLOCK_S3
|
||||
Adafruit_MCP23X17 mcp2;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_FRONTLIGHT
|
||||
|
@ -37,7 +35,7 @@ void setup()
|
|||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lockMcp(mcpMutex);
|
||||
if (mcp1.read1(3) == LOW)
|
||||
if (mcp1.digitalRead(3) == LOW)
|
||||
{
|
||||
preferences.putBool("wifiConfigured", false);
|
||||
preferences.remove("txPower");
|
||||
|
@ -48,7 +46,7 @@ void setup()
|
|||
}
|
||||
|
||||
{
|
||||
if (mcp1.read1(0) == LOW)
|
||||
if (mcp1.digitalRead(0) == LOW)
|
||||
{
|
||||
// Then loop forever to prevent anything else from writing to the screen
|
||||
while (true)
|
||||
|
@ -56,19 +54,15 @@ void setup()
|
|||
delay(1000);
|
||||
}
|
||||
}
|
||||
else if (mcp1.read1(1) == LOW)
|
||||
else if (mcp1.digitalRead(1) == LOW)
|
||||
{
|
||||
preferences.clear();
|
||||
queueLedEffect(LED_EFFECT_WIFI_ERASE_SETTINGS);
|
||||
nvs_flash_erase();
|
||||
delay(1000);
|
||||
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
setupWifi();
|
||||
// loadIcons();
|
||||
tryImprovSetup();
|
||||
|
||||
setupWebserver();
|
||||
|
||||
|
@ -95,11 +89,6 @@ void setup()
|
|||
setupBitaxeFetchTask();
|
||||
}
|
||||
|
||||
if (preferences.getBool("miningPoolStats", DEFAULT_MINING_POOL_STATS_ENABLED))
|
||||
{
|
||||
setupMiningPoolStatsFetchTask();
|
||||
}
|
||||
|
||||
setupButtonTask();
|
||||
setupOTA();
|
||||
|
||||
|
@ -114,10 +103,9 @@ void setup()
|
|||
#endif
|
||||
|
||||
forceFullRefresh();
|
||||
|
||||
}
|
||||
|
||||
void setupWifi()
|
||||
void tryImprovSetup()
|
||||
{
|
||||
WiFi.onEvent(WiFiEvent);
|
||||
WiFi.setAutoConnect(true);
|
||||
|
@ -141,7 +129,7 @@ void setupWifi()
|
|||
bool buttonPress = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lockMcp(mcpMutex);
|
||||
buttonPress = (mcp1.read1(2) == LOW);
|
||||
buttonPress = (mcp1.digitalRead(2) == LOW);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -152,10 +140,10 @@ void setupWifi()
|
|||
String softAP_SSID =
|
||||
String("BTClock" + String(mac[5], 16) + String(mac[1], 16));
|
||||
WiFi.setHostname(softAP_SSID.c_str());
|
||||
String softAP_password = replaceAmbiguousChars(
|
||||
String softAP_password =
|
||||
base64::encode(String(mac[2], 16) + String(mac[4], 16) +
|
||||
String(mac[5], 16) + String(mac[1], 16) + String(mac[3], 16))
|
||||
.substring(2, 10));
|
||||
String(mac[5], 16) + String(mac[1], 16))
|
||||
.substring(2, 10);
|
||||
|
||||
wm.setConfigPortalTimeout(preferences.getUInt("wpTimeout", DEFAULT_WP_TIMEOUT));
|
||||
wm.setWiFiAutoReconnect(false);
|
||||
|
@ -164,10 +152,10 @@ void setupWifi()
|
|||
|
||||
wm.setAPCallback([&](WiFiManager *wifiManager)
|
||||
{
|
||||
Serial.printf("Entered config mode:ip=%s, ssid='%s', pass='%s'\n",
|
||||
WiFi.softAPIP().toString().c_str(),
|
||||
wifiManager->getConfigPortalSSID().c_str(),
|
||||
softAP_password.c_str());
|
||||
// Serial.printf("Entered config mode:ip=%s, ssid='%s', pass='%s'\n",
|
||||
// WiFi.softAPIP().toString().c_str(),
|
||||
// wifiManager->getConfigPortalSSID().c_str(),
|
||||
// softAP_password.c_str());
|
||||
// delay(6000);
|
||||
setFgColor(GxEPD_BLACK);
|
||||
setBgColor(GxEPD_WHITE);
|
||||
|
@ -241,9 +229,6 @@ void setupWifi()
|
|||
// esp_task_wdt_deinit();
|
||||
// esp_task_wdt_reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
setFgColor(preferences.getUInt("fgColor", isWhiteVersion() ? GxEPD_BLACK : GxEPD_WHITE));
|
||||
setBgColor(preferences.getUInt("bgColor", isWhiteVersion() ? GxEPD_WHITE : GxEPD_BLACK));
|
||||
}
|
||||
|
@ -289,22 +274,9 @@ void setupPreferences()
|
|||
else
|
||||
setCurrentCurrency(CURRENCY_USD);
|
||||
|
||||
if (!preferences.isKey("flDisable")) {
|
||||
preferences.putBool("flDisable", isWhiteVersion() ? false : true);
|
||||
}
|
||||
|
||||
if (!preferences.isKey("gitReleaseUrl")) {
|
||||
preferences.putString("gitReleaseUrl", DEFAULT_GIT_RELEASE_URL);
|
||||
}
|
||||
|
||||
if (!preferences.isKey("fgColor")) {
|
||||
preferences.putUInt("fgColor", isWhiteVersion() ? GxEPD_BLACK : GxEPD_WHITE);
|
||||
preferences.putUInt("bgColor", isWhiteVersion() ? GxEPD_WHITE : GxEPD_BLACK);
|
||||
}
|
||||
|
||||
|
||||
addScreenMapping(SCREEN_BLOCK_HEIGHT, "Block Height");
|
||||
|
||||
|
||||
addScreenMapping(SCREEN_TIME, "Time");
|
||||
addScreenMapping(SCREEN_HALVING_COUNTDOWN, "Halving countdown");
|
||||
addScreenMapping(SCREEN_BLOCK_FEE_RATE, "Block Fee Rate");
|
||||
|
@ -313,6 +285,7 @@ void setupPreferences()
|
|||
addScreenMapping(SCREEN_BTC_TICKER, "Ticker");
|
||||
addScreenMapping(SCREEN_MARKET_CAP, "Market Cap");
|
||||
|
||||
|
||||
// addScreenMapping(SCREEN_SATS_PER_CURRENCY_USD, "Sats per USD");
|
||||
// addScreenMapping(SCREEN_BTC_TICKER_USD, "Ticker USD");
|
||||
// addScreenMapping(SCREEN_MARKET_CAP_USD, "Market Cap USD");
|
||||
|
@ -329,44 +302,82 @@ void setupPreferences()
|
|||
// screenNameMap[SCREEN_HALVING_COUNTDOWN] = "Halving countdown";
|
||||
// screenNameMap[SCREEN_MARKET_CAP] = "Market Cap";
|
||||
|
||||
// addCurrencyMappings(getActiveCurrencies());
|
||||
//addCurrencyMappings(getActiveCurrencies());
|
||||
|
||||
if (preferences.getBool("bitaxeEnabled", DEFAULT_BITAXE_ENABLED))
|
||||
{
|
||||
addScreenMapping(SCREEN_BITAXE_HASHRATE, "BitAxe Hashrate");
|
||||
addScreenMapping(SCREEN_BITAXE_BESTDIFF, "BitAxe Best Difficulty");
|
||||
}
|
||||
|
||||
if (preferences.getBool("miningPoolStats", DEFAULT_MINING_POOL_STATS_ENABLED))
|
||||
{
|
||||
addScreenMapping(SCREEN_MINING_POOL_STATS_HASHRATE, "Mining Pool Hashrate");
|
||||
if (getMiningPool()->supportsDailyEarnings()) {
|
||||
addScreenMapping(SCREEN_MINING_POOL_STATS_EARNINGS, "Mining Pool Earnings");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String replaceAmbiguousChars(String input)
|
||||
{
|
||||
const char *ambiguous = "1IlO0";
|
||||
const char *replacements = "LKQM8";
|
||||
// void addCurrencyMappings(const std::vector<std::string>& currencies)
|
||||
// {
|
||||
// for (const auto& currency : currencies)
|
||||
// {
|
||||
// int satsPerCurrencyScreen;
|
||||
// int btcTickerScreen;
|
||||
// int marketCapScreen;
|
||||
|
||||
for (int i = 0; i < strlen(ambiguous); i++)
|
||||
{
|
||||
input.replace(ambiguous[i], replacements[i]);
|
||||
}
|
||||
// // Determine the corresponding screen IDs based on the currency code
|
||||
// if (currency == "USD")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_USD;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_USD;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_USD;
|
||||
// }
|
||||
// else if (currency == "EUR")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_EUR;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_EUR;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_EUR;
|
||||
// }
|
||||
// else if (currency == "GBP")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_GBP;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_GBP;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_GBP;
|
||||
// }
|
||||
// else if (currency == "JPY")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_JPY;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_JPY;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_JPY;
|
||||
// }
|
||||
// else if (currency == "AUD")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_AUD;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_AUD;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_AUD;
|
||||
// }
|
||||
// else if (currency == "CAD")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_CAD;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_CAD;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_CAD;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// continue; // Unknown currency, skip it
|
||||
// }
|
||||
|
||||
return input;
|
||||
}
|
||||
// // Create the string locally to ensure it persists
|
||||
// std::string satsPerCurrencyString = "Sats per " + currency;
|
||||
// std::string btcTickerString = "Ticker " + currency;
|
||||
// std::string marketCapString = "Market Cap " + currency;
|
||||
|
||||
// // Pass the c_str() to the function
|
||||
// addScreenMapping(satsPerCurrencyScreen, satsPerCurrencyString.c_str());
|
||||
// addScreenMapping(btcTickerScreen, btcTickerString.c_str());
|
||||
// addScreenMapping(marketCapScreen, marketCapString.c_str());
|
||||
// }
|
||||
// }
|
||||
|
||||
void setupWebsocketClients(void *pvParameters)
|
||||
{
|
||||
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE))
|
||||
{
|
||||
V2Notify::setupV2Notify();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE)) {
|
||||
setupV2Notify();
|
||||
} else {
|
||||
setupBlockNotify();
|
||||
setupPriceNotify();
|
||||
}
|
||||
|
@ -398,12 +409,12 @@ std::vector<ScreenMapping> getScreenNameMap() { return screenMappings; }
|
|||
|
||||
void setupMcp()
|
||||
{
|
||||
#ifdef IS_BTCLOCK_V8
|
||||
#ifdef IS_BTCLOCK_S3
|
||||
const int mcp1AddrPins[] = {MCP1_A0_PIN, MCP1_A1_PIN, MCP1_A2_PIN};
|
||||
const int mcp1AddrValues[] = {LOW, LOW, LOW};
|
||||
|
||||
const int mcp2AddrPins[] = {MCP2_A0_PIN, MCP2_A1_PIN, MCP2_A2_PIN};
|
||||
const int mcp2AddrValues[] = {HIGH, LOW, LOW};
|
||||
const int mcp2AddrValues[] = {LOW, LOW, HIGH};
|
||||
|
||||
pinMode(MCP_RESET_PIN, OUTPUT);
|
||||
digitalWrite(MCP_RESET_PIN, HIGH);
|
||||
|
@ -430,28 +441,20 @@ void setupHardware()
|
|||
Serial.println(F("An Error has occurred while mounting LittleFS"));
|
||||
}
|
||||
|
||||
if (HW_REV == "REV_B_EPD_2_13" && !isWhiteVersion()) {
|
||||
Serial.println(F("Black Rev B"));
|
||||
}
|
||||
|
||||
if (!LittleFS.open("/index.html.gz", "r"))
|
||||
{
|
||||
Serial.println(F("Error loading WebUI"));
|
||||
}
|
||||
|
||||
// if (!LittleFS.exists("/qr.txt"))
|
||||
// {
|
||||
// File f = LittleFS.open("/qr.txt", "w");
|
||||
|
||||
// if(f) {
|
||||
// if (f.print("Hello")) {
|
||||
// Serial.println(F("Written QR to FS"));
|
||||
// Serial.printf("\nLittleFS free: %zu\n", LittleFS.totalBytes() - LittleFS.usedBytes());
|
||||
// }
|
||||
|
||||
// } else {
|
||||
// Serial.println(F("Can't write QR to FS"));
|
||||
// }
|
||||
|
||||
// f.close();
|
||||
// }
|
||||
|
||||
setupLeds();
|
||||
|
@ -466,9 +469,9 @@ void setupHardware()
|
|||
|
||||
Wire.begin(I2C_SDA_PIN, I2C_SCK_PIN, 400000);
|
||||
|
||||
if (!mcp1.begin())
|
||||
if (!mcp1.begin_I2C(0x20))
|
||||
{
|
||||
Serial.println(F("Error MCP23017 1"));
|
||||
Serial.println(F("Error MCP23017"));
|
||||
|
||||
// while (1)
|
||||
// ;
|
||||
|
@ -476,33 +479,30 @@ void setupHardware()
|
|||
else
|
||||
{
|
||||
pinMode(MCP_INT_PIN, INPUT_PULLUP);
|
||||
// mcp1.setupInterrupts(false, false, LOW);
|
||||
mcp1.enableControlRegister(MCP23x17_IOCR_ODR);
|
||||
|
||||
mcp1.mirrorInterrupts(true);
|
||||
mcp1.setupInterrupts(false, false, LOW);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
mcp1.pinMode1(i, INPUT_PULLUP);
|
||||
mcp1.enableInterrupt(i, LOW);
|
||||
mcp1.pinMode(i, INPUT_PULLUP);
|
||||
mcp1.setupInterruptPin(i, LOW);
|
||||
}
|
||||
#ifndef IS_BTCLOCK_V8
|
||||
#ifndef IS_BTCLOCK_S3
|
||||
for (int i = 8; i <= 14; i++)
|
||||
{
|
||||
mcp1.pinMode1(i, OUTPUT);
|
||||
mcp1.pinMode(i, OUTPUT);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef IS_HW_REV_B
|
||||
pinMode(39, INPUT_PULLDOWN);
|
||||
pinMode(39, INPUT_PULLUP);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef IS_BTCLOCK_V8
|
||||
if (!mcp2.begin())
|
||||
#ifdef IS_BTCLOCK_S3
|
||||
if (!mcp2.begin_I2C(0x21))
|
||||
{
|
||||
Serial.println(F("Error MCP23017 2"));
|
||||
Serial.println(F("Error MCP23017"));
|
||||
|
||||
// while (1)
|
||||
// ;
|
||||
|
@ -519,7 +519,7 @@ void setupHardware()
|
|||
{
|
||||
Serial.println(F("Found BH1750"));
|
||||
hasLuxSensor = true;
|
||||
bh1750.begin(BH1750::CONTINUOUS_HIGH_RES_MODE, 0x5C);
|
||||
bh1750.begin(BH1750::CONTINUOUS_LOW_RES_MODE, 0x5C);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -676,7 +676,6 @@ String getHwRev()
|
|||
bool isWhiteVersion()
|
||||
{
|
||||
#ifdef IS_HW_REV_B
|
||||
pinMode(39, INPUT_PULLDOWN);
|
||||
return digitalRead(39);
|
||||
#else
|
||||
return false;
|
||||
|
@ -740,28 +739,4 @@ bool isActiveCurrency(std::string ¤cy)
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* getFirmwareFilename() {
|
||||
if (HW_REV == "REV_B_EPD_2_13") {
|
||||
return "btclock_rev_b_213epd_firmware.bin";
|
||||
} else if (HW_REV == "REV_A_EPD_2_13") {
|
||||
return "lolin_s3_mini_213epd_firmware.bin";
|
||||
} else if (HW_REV == "REV_A_EPD_2_9") {
|
||||
return "lolin_s3_mini_29epd_firmware.bin";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
const char* getWebUiFilename() {
|
||||
if (HW_REV == "REV_B_EPD_2_13") {
|
||||
return "littlefs_8MB.bin";
|
||||
} else if (HW_REV == "REV_A_EPD_2_13") {
|
||||
return "littlefs_4MB.bin";
|
||||
} else if (HW_REV == "REV_A_EPD_2_9") {
|
||||
return "littlefs_4MB.bin";
|
||||
} else {
|
||||
return "littlefs_4MB.bin";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <MCP23017.h>
|
||||
#include <Adafruit_MCP23X17.h>
|
||||
#include <Arduino.h>
|
||||
#include <Preferences.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <base64.h>
|
||||
#include <esp_task_wdt.h>
|
||||
#include <nvs_flash.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "lib/block_notify.hpp"
|
||||
|
@ -18,7 +18,6 @@
|
|||
#include "lib/ota.hpp"
|
||||
#include "lib/nostr_notify.hpp"
|
||||
#include "lib/bitaxe_fetch.hpp"
|
||||
#include "lib/mining_pool_stats_fetch.hpp"
|
||||
|
||||
#include "lib/v2_notify.hpp"
|
||||
|
||||
|
@ -33,6 +32,7 @@
|
|||
|
||||
#define NTP_SERVER "pool.ntp.org"
|
||||
#define DEFAULT_TIME_OFFSET_SECONDS 3600
|
||||
#define USER_AGENT "BTClock/3.0"
|
||||
#ifndef MCP_DEV_ADDR
|
||||
#define MCP_DEV_ADDR 0x20
|
||||
#endif
|
||||
|
@ -44,7 +44,7 @@ uint getLastTimeSync();
|
|||
void setupPreferences();
|
||||
void setupWebsocketClients(void *pvParameters);
|
||||
void setupHardware();
|
||||
void setupWifi();
|
||||
void tryImprovSetup();
|
||||
void setupTimers();
|
||||
void finishSetup();
|
||||
void setupMcp();
|
||||
|
@ -81,8 +81,4 @@ void addScreenMapping(int value, const char* name);
|
|||
// void addScreenMapping(int value, const String& name);
|
||||
// void addScreenMapping(int value, const std::string& name);
|
||||
|
||||
int findScreenIndexByValue(int value);
|
||||
String replaceAmbiguousChars(String input);
|
||||
const char* getFirmwareFilename();
|
||||
const char* getWebUiFilename();
|
||||
// void loadIcons();
|
||||
int findScreenIndexByValue(int value);
|
|
@ -18,9 +18,6 @@
|
|||
#define DEFAULT_DISABLE_FL false
|
||||
#define DEFAULT_OWN_DATA_SOURCE true
|
||||
#define DEFAULT_STAGING_SOURCE false
|
||||
#define DEFAULT_MOW_MODE false
|
||||
#define DEFAULT_SUFFIX_SHARE_DOT false
|
||||
|
||||
#define DEFAULT_V2_SOURCE_CURRENCY CURRENCY_USD
|
||||
|
||||
|
||||
|
@ -45,8 +42,6 @@
|
|||
#define DEFAULT_FL_EFFECT_DELAY 15
|
||||
|
||||
#define DEFAULT_LUX_LIGHT_TOGGLE 128
|
||||
#define DEFAULT_FL_OFF_WHEN_DARK true
|
||||
|
||||
#define DEFAULT_FL_ALWAYS_ON false
|
||||
#define DEFAULT_FL_FLASH_ON_UPDATE false
|
||||
|
||||
|
@ -58,22 +53,11 @@
|
|||
#define DEFAULT_BITAXE_ENABLED false
|
||||
#define DEFAULT_BITAXE_HOSTNAME "bitaxe1"
|
||||
|
||||
#define DEFAULT_MINING_POOL_STATS_ENABLED false
|
||||
#define DEFAULT_MINING_POOL_NAME "ocean"
|
||||
#define DEFAULT_MINING_POOL_USER "38Qkkei3SuF1Eo45BaYmRHUneRD54yyTFy" // Random actual Ocean hasher
|
||||
|
||||
#define DEFAULT_ZAP_NOTIFY_ENABLED false
|
||||
#define DEFAULT_ZAP_NOTIFY_PUBKEY "b5127a08cf33616274800a4387881a9f98e04b9c37116e92de5250498635c422"
|
||||
#define DEFAULT_LED_FLASH_ON_ZAP true
|
||||
#define DEFAULT_FL_FLASH_ON_ZAP true
|
||||
|
||||
#define DEFAULT_HTTP_AUTH_ENABLED false
|
||||
#define DEFAULT_HTTP_AUTH_USERNAME "btclock"
|
||||
#define DEFAULT_HTTP_AUTH_PASSWORD "satoshi"
|
||||
|
||||
#define DEFAULT_ACTIVE_CURRENCIES "USD,EUR,JPY"
|
||||
|
||||
#define DEFAULT_GIT_RELEASE_URL "https://git.btclock.dev/api/v1/repos/btclock/btclock_v3/releases/latest"
|
||||
#define DEFAULT_VERTICAL_DESC true
|
||||
|
||||
#define DEFAULT_MINING_POOL_LOGOS_URL "https://git.btclock.dev/btclock/mining-pool-logos/raw/branch/main"
|
||||
#define DEFAULT_ACTIVE_CURRENCIES "USD,EUR,JPY"
|
230
src/lib/epd.cpp
230
src/lib/epd.cpp
|
@ -30,7 +30,7 @@ MCP23X17_Pin EPD_RESET_MPD[NUM_SCREENS] = {
|
|||
};
|
||||
|
||||
Native_Pin EPD_DC = Native_Pin(14);
|
||||
#elif IS_BTCLOCK_V8
|
||||
#elif IS_BTCLOCK_S3
|
||||
Native_Pin EPD_DC = Native_Pin(38);
|
||||
|
||||
MCP23X17_Pin EPD_BUSY[NUM_SCREENS] = {
|
||||
|
@ -104,8 +104,8 @@ GxEPD2_BW<EPD_CLASS, EPD_CLASS::HEIGHT> displays[NUM_SCREENS] = {
|
|||
EPD_CLASS(&EPD_CS[4], &EPD_DC, &EPD_RESET_MPD[4], &EPD_BUSY[4]),
|
||||
EPD_CLASS(&EPD_CS[5], &EPD_DC, &EPD_RESET_MPD[5], &EPD_BUSY[5]),
|
||||
EPD_CLASS(&EPD_CS[6], &EPD_DC, &EPD_RESET_MPD[6], &EPD_BUSY[6]),
|
||||
#ifdef IS_BTCLOCK_V8
|
||||
EPD_CLASS(&EPD_CS[7], &EPD_DC, &EPD_RESET_MPD[7], &EPD_BUSY[7]),
|
||||
#ifdef IS_BTCLOCK_S3
|
||||
EPD_CLASS(&EPD_CS[7], &EPD_DC, &EPD_RESET_MPD[6], &EPD_BUSY[7]),
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -132,15 +132,6 @@ std::mutex epdMutex[NUM_SCREENS];
|
|||
|
||||
uint8_t qrcode[800];
|
||||
|
||||
#ifdef IS_BTCLOCK_V8
|
||||
#define EPD_TASK_STACK_SIZE 4096
|
||||
#else
|
||||
#define EPD_TASK_STACK_SIZE 2048
|
||||
#endif
|
||||
|
||||
#define BUSY_TIMEOUT_COUNT 200
|
||||
#define BUSY_RETRY_DELAY pdMS_TO_TICKS(10)
|
||||
|
||||
void forceFullRefresh()
|
||||
{
|
||||
for (uint i = 0; i < NUM_SCREENS; i++)
|
||||
|
@ -179,7 +170,7 @@ void setupDisplays()
|
|||
|
||||
updateQueue = xQueueCreate(UPDATE_QUEUE_SIZE, sizeof(UpdateDisplayTaskItem));
|
||||
|
||||
xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", EPD_TASK_STACK_SIZE*2, NULL, 11, NULL);
|
||||
xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", 2048, NULL, 11, NULL);
|
||||
|
||||
for (uint i = 0; i < NUM_SCREENS; i++)
|
||||
{
|
||||
|
@ -189,25 +180,22 @@ void setupDisplays()
|
|||
int *taskParam = new int;
|
||||
*taskParam = i;
|
||||
|
||||
xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), EPD_TASK_STACK_SIZE, taskParam,
|
||||
xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), 2048, taskParam,
|
||||
11, &tasks[i]); // create task
|
||||
}
|
||||
|
||||
// Hold lower button to enable "storage mode" (prevents burn-in of ePaper displays)
|
||||
if (mcp1.read1(0) == LOW)
|
||||
if (mcp1.digitalRead(0) == LOW)
|
||||
{
|
||||
setFgColor(GxEPD_BLACK);
|
||||
setBgColor(GxEPD_WHITE);
|
||||
|
||||
epdContent.fill("");
|
||||
epdContent = {" ", " ", " ", " ", " ", " ", " "};
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef IS_BTCLOCK_V8
|
||||
epdContent = {"B", "T", "C", "L", "O", "C", "K", "v8"};
|
||||
#else
|
||||
|
||||
epdContent = {"B", "T", "C", "L", "O", "C", "K"};
|
||||
#endif
|
||||
}
|
||||
|
||||
setEpdContent(epdContent);
|
||||
|
@ -278,10 +266,7 @@ void prepareDisplayUpdateTask(void *pvParameters)
|
|||
}
|
||||
else if (epdContent[epdIndex].startsWith(F("mdi")))
|
||||
{
|
||||
bool updated = renderIcon(epdIndex, epdContent[epdIndex], updatePartial);
|
||||
if (!updated) {
|
||||
continue;
|
||||
}
|
||||
renderIcon(epdIndex, epdContent[epdIndex], updatePartial);
|
||||
}
|
||||
else if (epdContent[epdIndex].length() > 5)
|
||||
{
|
||||
|
@ -289,10 +274,7 @@ void prepareDisplayUpdateTask(void *pvParameters)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (epdContent[epdIndex].length() == 2) {
|
||||
showChars(epdIndex, epdContent[epdIndex], updatePartial, &FONT_BIG);
|
||||
}
|
||||
else if (epdContent[epdIndex].length() > 1 && epdContent[epdIndex].indexOf(".") == -1)
|
||||
if (epdContent[epdIndex].length() > 1 && epdContent[epdIndex].indexOf(".") == -1)
|
||||
{
|
||||
if (epdContent[epdIndex].equals("STS"))
|
||||
{
|
||||
|
@ -379,11 +361,7 @@ extern "C" void updateDisplay(void *pvParameters) noexcept
|
|||
void splitText(const uint dispNum, const String &top, const String &bottom,
|
||||
bool partial)
|
||||
{
|
||||
if(preferences.getBool("verticalDesc", DEFAULT_VERTICAL_DESC) && dispNum == 0) {
|
||||
displays[dispNum].setRotation(1);
|
||||
} else {
|
||||
displays[dispNum].setRotation(2);
|
||||
}
|
||||
displays[dispNum].setRotation(2);
|
||||
displays[dispNum].setFont(&FONT_SMALL);
|
||||
displays[dispNum].setTextColor(getFgColor());
|
||||
|
||||
|
@ -420,83 +398,80 @@ void splitText(const uint dispNum, const String &top, const String &bottom,
|
|||
displays[dispNum].print(bottom);
|
||||
}
|
||||
|
||||
// Consolidate common display setup code into a helper function
|
||||
void setupDisplay(const uint dispNum, const GFXfont *font) {
|
||||
displays[dispNum].setRotation(2);
|
||||
displays[dispNum].setFont(font);
|
||||
displays[dispNum].setTextColor(getFgColor());
|
||||
displays[dispNum].fillScreen(getBgColor());
|
||||
}
|
||||
|
||||
void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font) {
|
||||
String str(chr);
|
||||
if (chr == '.') {
|
||||
str = "!";
|
||||
}
|
||||
|
||||
setupDisplay(dispNum, font);
|
||||
|
||||
int16_t tbx, tby;
|
||||
uint16_t tbw, tbh;
|
||||
displays[dispNum].getTextBounds(str, 0, 0, &tbx, &tby, &tbw, &tbh);
|
||||
|
||||
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
|
||||
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
|
||||
|
||||
displays[dispNum].setCursor(x, y);
|
||||
displays[dispNum].print(str);
|
||||
|
||||
if (chr == '.') {
|
||||
displays[dispNum].fillRect(x, y, displays[dispNum].width(),
|
||||
round(displays[dispNum].height() * 0.9), getBgColor());
|
||||
}
|
||||
}
|
||||
|
||||
int16_t calculateDescent(const GFXfont *font) {
|
||||
int16_t maxDescent = 0;
|
||||
for (uint16_t i = font->first; i <= font->last; i++) {
|
||||
GFXglyph *glyph = &font->glyph[i - font->first];
|
||||
int16_t descent = glyph->yOffset;
|
||||
if (descent > maxDescent) {
|
||||
maxDescent = descent;
|
||||
}
|
||||
}
|
||||
return maxDescent;
|
||||
}
|
||||
|
||||
void showChars(const uint dispNum, const String &chars, bool partial,
|
||||
void showDigit(const uint dispNum, char chr, bool partial,
|
||||
const GFXfont *font)
|
||||
{
|
||||
setupDisplay(dispNum, font);
|
||||
|
||||
String str(chr);
|
||||
|
||||
if (chr == '.')
|
||||
{
|
||||
str = "!";
|
||||
}
|
||||
displays[dispNum].setRotation(2);
|
||||
displays[dispNum].setFont(font);
|
||||
displays[dispNum].setTextColor(getFgColor());
|
||||
int16_t tbx, tby;
|
||||
uint16_t tbw, tbh;
|
||||
displays[dispNum].getTextBounds(chars, 0, 0, &tbx, &tby, &tbw, &tbh);
|
||||
|
||||
displays[dispNum].getTextBounds(str, 0, 0, &tbx, &tby, &tbw, &tbh);
|
||||
|
||||
// center the bounding box by transposition of the origin:
|
||||
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
|
||||
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
|
||||
|
||||
for (int i = 0; i < chars.length(); i++) {
|
||||
char c = chars[i];
|
||||
if (c == '.' || c == ',') {
|
||||
// For the dot, calculate its specific descent
|
||||
GFXglyph *dotGlyph = &font->glyph[c -font->first];
|
||||
int16_t dotDescent = dotGlyph->yOffset;
|
||||
|
||||
// Draw the dot with adjusted y-position
|
||||
displays[dispNum].setCursor(x, y + dotDescent + dotGlyph->height + 8);
|
||||
displays[dispNum].print(c);
|
||||
} else {
|
||||
// For other characters, use the original y-position
|
||||
displays[dispNum].setCursor(x, y);
|
||||
displays[dispNum].print(c);
|
||||
}
|
||||
|
||||
// Move x-position for the next character
|
||||
x += font->glyph[c - font->first].xAdvance;
|
||||
// if (str.equals("."))
|
||||
// {
|
||||
// // int16_t yAdvance = font->yAdvance;
|
||||
// // uint8_t charIndex = 46 - font->first;
|
||||
// // GFXglyph *glyph = (&font->glyph)[charIndex];
|
||||
// int16_t tbx2, tby2;
|
||||
// uint16_t tbw2, tbh2;
|
||||
// displays[dispNum].getTextBounds(".!", 0, 0, &tbx2, &tby2, &tbw2, &tbh2);
|
||||
|
||||
// y = ((displays[dispNum].height() - tbh2) / 2) - tby2;
|
||||
// // Serial.print("yAdvance");
|
||||
// // Serial.println(yAdvance);
|
||||
// // if (glyph != nullptr) {
|
||||
// // Serial.print("height");
|
||||
// // Serial.println(glyph->height);
|
||||
// // Serial.print("yOffset");
|
||||
// // Serial.println(glyph->yOffset);
|
||||
// // }
|
||||
|
||||
// // y = 250-99+18+19;
|
||||
// }
|
||||
|
||||
displays[dispNum].fillScreen(getBgColor());
|
||||
|
||||
displays[dispNum].setCursor(x, y);
|
||||
displays[dispNum].print(str);
|
||||
|
||||
if (chr == '.')
|
||||
{
|
||||
displays[dispNum].fillRect(x, y, displays[dispNum].width(), round(displays[dispNum].height() * 0.9), getBgColor());
|
||||
}
|
||||
|
||||
// displays[dispNum].setCursor(10, 3);
|
||||
// displays[dispNum].setFont(&FONT_SMALL);
|
||||
// displays[dispNum].setTextColor(getFgColor());
|
||||
// displays[dispNum].println("Y = " + y);
|
||||
}
|
||||
|
||||
void showChars(const uint dispNum, const String &chars, bool partial,
|
||||
const GFXfont *font)
|
||||
{
|
||||
displays[dispNum].setRotation(2);
|
||||
displays[dispNum].setFont(font);
|
||||
displays[dispNum].setTextColor(getFgColor());
|
||||
int16_t tbx, tby;
|
||||
uint16_t tbw, tbh;
|
||||
displays[dispNum].getTextBounds(chars, 0, 0, &tbx, &tby, &tbw, &tbh);
|
||||
// center the bounding box by transposition of the origin:
|
||||
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
|
||||
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
|
||||
displays[dispNum].fillScreen(getBgColor());
|
||||
displays[dispNum].setCursor(x, y);
|
||||
displays[dispNum].print(chars);
|
||||
}
|
||||
|
||||
int getBgColor() { return bgColor; }
|
||||
|
@ -542,55 +517,25 @@ void renderText(const uint dispNum, const String &text, bool partial)
|
|||
}
|
||||
}
|
||||
|
||||
bool renderIcon(const uint dispNum, const String &text, bool partial)
|
||||
void renderIcon(const uint dispNum, const String &text, bool partial)
|
||||
{
|
||||
displays[dispNum].setRotation(2);
|
||||
|
||||
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(),
|
||||
displays[dispNum].height());
|
||||
displays[dispNum].fillScreen(getBgColor());
|
||||
displays[dispNum].setTextColor(getFgColor());
|
||||
displays[dispNum].fillScreen(getBgColor());
|
||||
displays[dispNum].setTextColor(getFgColor());
|
||||
|
||||
uint iconIndex = 0;
|
||||
uint width = 122;
|
||||
uint height = 122;
|
||||
if (text.endsWith("rocket")) {
|
||||
iconIndex = 1;
|
||||
}
|
||||
else if (text.endsWith("lnbolt")) {
|
||||
iconIndex = 2;
|
||||
}
|
||||
else if (text.endsWith("bitaxe")) {
|
||||
width = 88;
|
||||
height = 220;
|
||||
|
||||
if (text.endsWith("lnbolt")) {
|
||||
iconIndex = 3;
|
||||
}
|
||||
else if (text.endsWith("miningpool")) {
|
||||
LogoData logo = getMiningPoolLogo();
|
||||
|
||||
if (logo.size == 0) {
|
||||
Serial.println("No logo found");
|
||||
return false;
|
||||
}
|
||||
|
||||
int x_offset = (displays[dispNum].width() - logo.width) / 2;
|
||||
int y_offset = (displays[dispNum].height() - logo.height) / 2;
|
||||
// Close the file
|
||||
|
||||
displays[dispNum].drawInvertedBitmap(x_offset,y_offset, logo.data, logo.width, logo.height, getFgColor());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int x_offset = (displays[dispNum].width() - width) / 2;
|
||||
int y_offset = (displays[dispNum].height() - height) / 2;
|
||||
|
||||
|
||||
displays[dispNum].drawInvertedBitmap(x_offset,y_offset, epd_icons_allArray[iconIndex], width, height, getFgColor());
|
||||
|
||||
return true;
|
||||
// displays[dispNum].drawInvertedBitmap(0,0, getOceanIcon(), 122, 250, getFgColor());
|
||||
|
||||
displays[dispNum].drawInvertedBitmap(0,0, epd_icons_allArray[iconIndex], 122, 250, getFgColor());
|
||||
|
||||
}
|
||||
|
||||
|
@ -637,12 +582,15 @@ void waitUntilNoneBusy()
|
|||
while (EPD_BUSY[i].digitalRead())
|
||||
{
|
||||
count++;
|
||||
vTaskDelay(BUSY_RETRY_DELAY);
|
||||
|
||||
if (count == BUSY_TIMEOUT_COUNT) {
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
} else if (count > BUSY_TIMEOUT_COUNT + 5) {
|
||||
log_e("Display %d busy timeout", i);
|
||||
vTaskDelay(10);
|
||||
if (count == 200)
|
||||
{
|
||||
// displays[i].init(0, false);
|
||||
vTaskDelay(100);
|
||||
}
|
||||
else if (count > 205)
|
||||
{
|
||||
Serial.printf("Busy timeout %d", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include <Fonts/FreeSansBold9pt7b.h>
|
||||
#include <GxEPD2_BW.h>
|
||||
|
||||
|
||||
#include <mcp23x17_pin.hpp>
|
||||
#include <mutex>
|
||||
#include <native_pin.hpp>
|
||||
|
@ -14,7 +13,6 @@
|
|||
#include "lib/config.hpp"
|
||||
#include "lib/shared.hpp"
|
||||
#include "icons/icons.h"
|
||||
#include "mining_pool_stats_fetch.hpp"
|
||||
|
||||
#ifdef USE_QR
|
||||
#include "qrcodegen.h"
|
||||
|
@ -45,7 +43,7 @@ int getFgColor();
|
|||
void setBgColor(int color);
|
||||
void setFgColor(int color);
|
||||
|
||||
bool renderIcon(const uint dispNum, const String &text, bool partial);
|
||||
void renderIcon(const uint dispNum, const String &text, bool partial);
|
||||
void renderText(const uint dispNum, const String &text, bool partial);
|
||||
void renderQr(const uint dispNum, const String &text, bool partial);
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ bool flInTransition = false;
|
|||
|
||||
void frontlightFlash(int flDelayTime)
|
||||
{
|
||||
if (preferences.getBool("flDisable"))
|
||||
if (preferences.getBool("flDisable", DEFAULT_DISABLE_FL))
|
||||
return;
|
||||
|
||||
if (frontlightOn)
|
||||
|
@ -68,7 +68,7 @@ void frontlightFadeInAll(int flDelayTime)
|
|||
|
||||
void frontlightFadeInAll(int flDelayTime, bool staggered)
|
||||
{
|
||||
if (preferences.getBool("flDisable"))
|
||||
if (preferences.getBool("flDisable", DEFAULT_DISABLE_FL))
|
||||
return;
|
||||
if (frontlightIsOn())
|
||||
return;
|
||||
|
@ -120,7 +120,7 @@ void frontlightFadeOutAll(int flDelayTime)
|
|||
|
||||
void frontlightFadeOutAll(int flDelayTime, bool staggered)
|
||||
{
|
||||
if (preferences.getBool("flDisable"))
|
||||
if (preferences.getBool("flDisable", DEFAULT_DISABLE_FL))
|
||||
return;
|
||||
if (!frontlightIsOn())
|
||||
return;
|
||||
|
@ -186,7 +186,7 @@ bool frontlightIsOn()
|
|||
|
||||
void frontlightFadeIn(uint num, int flDelayTime)
|
||||
{
|
||||
if (preferences.getBool("flDisable"))
|
||||
if (preferences.getBool("flDisable", DEFAULT_DISABLE_FL))
|
||||
return;
|
||||
for (int dutyCycle = 0; dutyCycle <= preferences.getUInt("flMaxBrightness"); dutyCycle += 5)
|
||||
{
|
||||
|
@ -197,7 +197,7 @@ void frontlightFadeIn(uint num, int flDelayTime)
|
|||
|
||||
void frontlightFadeOut(uint num, int flDelayTime)
|
||||
{
|
||||
if (preferences.getBool("flDisable"))
|
||||
if (preferences.getBool("flDisable", DEFAULT_DISABLE_FL))
|
||||
return;
|
||||
if (!frontlightIsOn())
|
||||
return;
|
||||
|
@ -285,7 +285,7 @@ void ledTask(void *parameter)
|
|||
#ifdef HAS_FRONTLIGHT
|
||||
bool frontlightWasOn = false;
|
||||
|
||||
if (preferences.getBool("flFlashOnZap", DEFAULT_FL_FLASH_ON_ZAP))
|
||||
if (preferences.getBool("flFlashOnUpd", DEFAULT_FL_FLASH_ON_UPDATE))
|
||||
{
|
||||
if (frontlightOn)
|
||||
{
|
||||
|
@ -298,16 +298,11 @@ void ledTask(void *parameter)
|
|||
}
|
||||
}
|
||||
#endif
|
||||
for (int flash = 0; flash < random(7, 10); flash++)
|
||||
{
|
||||
lightningStrike();
|
||||
delay(random(50, 150));
|
||||
}
|
||||
// blinkDelayColor(250, 3, 142, 48, 235);
|
||||
// blinkDelayTwoColor(250, 3, pixels.Color(142, 48, 235),
|
||||
// pixels.Color(169, 21, 255));
|
||||
blinkDelayColor(250, 3, 142, 48, 235);
|
||||
// blinkDelayTwoColor(250, 3, pixels.Color(142, 48, 235),
|
||||
// pixels.Color(169, 21, 255));
|
||||
#ifdef HAS_FRONTLIGHT
|
||||
if (preferences.getBool("flFlashOnZap", DEFAULT_FL_FLASH_ON_ZAP))
|
||||
if (preferences.getBool("flFlashOnUpd", DEFAULT_FL_FLASH_ON_UPDATE))
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
if (frontlightWasOn)
|
||||
|
@ -323,8 +318,6 @@ void ledTask(void *parameter)
|
|||
break;
|
||||
}
|
||||
case LED_FLASH_UPDATE:
|
||||
blinkDelayTwoColor(250, 3, pixels.Color(0, 230, 0),
|
||||
pixels.Color(230, 230, 0));
|
||||
break;
|
||||
case LED_FLASH_BLOCK_NOTIFY:
|
||||
{
|
||||
|
@ -675,29 +668,4 @@ void ledTheaterChaseRainbow(int wait)
|
|||
}
|
||||
}
|
||||
|
||||
void lightningStrike()
|
||||
{
|
||||
uint32_t PURPLE = pixels.Color(128, 0, 128);
|
||||
uint32_t YELLOW = pixels.Color(255, 226, 41);
|
||||
|
||||
// Randomly choose which LEDs to light up
|
||||
for (int i = 0; i < pixels.numPixels(); i++)
|
||||
{
|
||||
if (random(2) == 0)
|
||||
{ // 50% chance for each LED
|
||||
pixels.setPixelColor(i, YELLOW);
|
||||
}
|
||||
else
|
||||
{
|
||||
pixels.setPixelColor(i, PURPLE);
|
||||
}
|
||||
}
|
||||
pixels.show();
|
||||
|
||||
delay(random(10, 50)); // Flash duration
|
||||
|
||||
// Return to purple background
|
||||
// setAllPixels(PURPLE);
|
||||
}
|
||||
|
||||
Adafruit_NeoPixel getPixels() { return pixels; }
|
||||
|
|
|
@ -61,7 +61,6 @@ void ledRainbow(int wait);
|
|||
void ledTheaterChaseRainbow(int wait);
|
||||
void ledTheaterChase(uint32_t color, int wait);
|
||||
Adafruit_NeoPixel getPixels();
|
||||
void lightningStrike();
|
||||
|
||||
#ifdef HAS_FRONTLIGHT
|
||||
void frontlightFlash(int flDelayTime);
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
#include "brains_pool.hpp"
|
||||
|
||||
void BraiinsPool::prepareRequest(HTTPClient& http) const {
|
||||
http.addHeader("Pool-Auth-Token", poolUser.c_str());
|
||||
}
|
||||
|
||||
std::string BraiinsPool::getApiUrl() const {
|
||||
return "https://pool.braiins.com/accounts/profile/json/btc/";
|
||||
}
|
||||
|
||||
PoolStats BraiinsPool::parseResponse(const JsonDocument &doc) const
|
||||
{
|
||||
if (doc["btc"].isNull()) {
|
||||
return PoolStats{
|
||||
.hashrate = "0",
|
||||
.dailyEarnings = 0
|
||||
};
|
||||
}
|
||||
|
||||
std::string unit = doc["btc"]["hash_rate_unit"].as<std::string>();
|
||||
|
||||
static const std::unordered_map<std::string, int> multipliers = {
|
||||
{"Zh/s", 21}, {"Eh/s", 18}, {"Ph/s", 15}, {"Th/s", 12}, {"Gh/s", 9}, {"Mh/s", 6}, {"Kh/s", 3}};
|
||||
|
||||
int multiplier = multipliers.at(unit);
|
||||
float hashValue = doc["btc"]["hash_rate_5m"].as<float>();
|
||||
|
||||
return PoolStats{
|
||||
.hashrate = std::to_string(static_cast<int>(std::round(hashValue))) + std::string(multiplier, '0'),
|
||||
.dailyEarnings = static_cast<int64_t>(doc["btc"]["today_reward"].as<float>() * 100000000)};
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#include <icons/icons.h>
|
||||
#include <utils.hpp>
|
||||
|
||||
class BraiinsPool : public MiningPoolInterface
|
||||
{
|
||||
public:
|
||||
void setPoolUser(const std::string &user) override { poolUser = user; }
|
||||
void prepareRequest(HTTPClient &http) const override;
|
||||
std::string getApiUrl() const override;
|
||||
PoolStats parseResponse(const JsonDocument &doc) const override;
|
||||
bool supportsDailyEarnings() const override { return true; }
|
||||
bool hasLogo() const override { return true; }
|
||||
std::string getDisplayLabel() const override { return "BRAIINS/POOL"; } // Fallback if needed
|
||||
std::string getDailyEarningsLabel() const override { return "sats/earned"; }
|
||||
std::string getLogoFilename() const override {
|
||||
return "braiins.bin";
|
||||
}
|
||||
|
||||
std::string getPoolName() const override {
|
||||
return "braiins";
|
||||
}
|
||||
|
||||
int getLogoWidth() const override {
|
||||
return 37;
|
||||
}
|
||||
|
||||
int getLogoHeight() const override {
|
||||
return 230;
|
||||
}
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
// src/noderunners/noderunners_pool.cpp
|
||||
#include "gobrrr_pool.hpp"
|
||||
|
||||
std::string GoBrrrPool::getApiUrl() const {
|
||||
return "https://pool.gobrrr.me/api/client/" + poolUser;
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#include "lib/mining_pool/public_pool/public_pool.hpp"
|
||||
|
||||
#include <icons/icons.h>
|
||||
|
||||
class GoBrrrPool : public PublicPool {
|
||||
public:
|
||||
std::string getApiUrl() const override;
|
||||
bool hasLogo() const override { return true; }
|
||||
std::string getDisplayLabel() const override { return "GOBRRR/POOL"; }
|
||||
|
||||
std::string getLogoFilename() const override {
|
||||
return "gobrrr.bin";
|
||||
}
|
||||
|
||||
std::string getPoolName() const override {
|
||||
return "gobrrr_pool";
|
||||
}
|
||||
|
||||
int getLogoWidth() const override {
|
||||
return 122;
|
||||
}
|
||||
|
||||
int getLogoHeight() const override {
|
||||
return 122;
|
||||
}
|
||||
};
|
|
@ -1,11 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <stddef.h>
|
||||
|
||||
struct LogoData {
|
||||
const uint8_t* data;
|
||||
size_t width;
|
||||
size_t height;
|
||||
size_t size;
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
#include "mining_pool_interface.hpp"
|
||||
#include "pool_factory.hpp"
|
||||
|
||||
LogoData MiningPoolInterface::getLogo() const {
|
||||
if (!hasLogo()) {
|
||||
return LogoData{nullptr, 0, 0, 0};
|
||||
}
|
||||
|
||||
// Check if logo exists
|
||||
String logoPath = String(PoolFactory::getLogosDir()) + "/" + String(getPoolName().c_str()) + "_logo.bin";
|
||||
|
||||
if (!LittleFS.exists(logoPath)) {
|
||||
return LogoData{nullptr, 0, 0, 0};
|
||||
}
|
||||
|
||||
// Now load the logo (whether it was just downloaded or already existed)
|
||||
return PoolFactory::loadLogoFromFS(getPoolName(), this);
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <HTTPClient.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include "pool_stats.hpp"
|
||||
#include "logo_data.hpp"
|
||||
#include "lib/shared.hpp"
|
||||
|
||||
class MiningPoolInterface {
|
||||
public:
|
||||
virtual ~MiningPoolInterface() = default;
|
||||
virtual void setPoolUser(const std::string& user) = 0;
|
||||
virtual void prepareRequest(HTTPClient& http) const = 0;
|
||||
virtual std::string getApiUrl() const = 0;
|
||||
virtual PoolStats parseResponse(const JsonDocument& doc) const = 0;
|
||||
virtual bool hasLogo() const = 0;
|
||||
virtual LogoData getLogo() const;
|
||||
virtual std::string getDisplayLabel() const = 0;
|
||||
virtual bool supportsDailyEarnings() const = 0;
|
||||
virtual std::string getDailyEarningsLabel() const = 0;
|
||||
virtual std::string getLogoFilename() const { return ""; }
|
||||
virtual std::string getPoolName() const = 0;
|
||||
virtual int getLogoWidth() const { return 0; }
|
||||
virtual int getLogoHeight() const { return 0; }
|
||||
std::string getLogoUrl() const {
|
||||
if (!hasLogo() || getLogoFilename().empty()) {
|
||||
return "";
|
||||
}
|
||||
std::string baseUrl = preferences.getString("poolLogosUrl", DEFAULT_MINING_POOL_LOGOS_URL).c_str();
|
||||
return baseUrl + "/" + getLogoFilename().c_str();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string poolUser;
|
||||
};
|
|
@ -1,95 +0,0 @@
|
|||
#include "mining_pool_stats_handler.hpp"
|
||||
|
||||
std::array<std::string, NUM_SCREENS> parseMiningPoolStatsHashRate(const std::string& hashrate, const MiningPoolInterface& pool)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> ret;
|
||||
ret.fill(""); // Initialize all elements to empty strings
|
||||
std::string label;
|
||||
std::string output;
|
||||
|
||||
parseHashrateString(hashrate, label, output, 4);
|
||||
|
||||
std::size_t textLength = output.length();
|
||||
// Calculate the position where the digits should start
|
||||
// Account for the position of the mining pool logo and the hashrate label
|
||||
std::size_t startIndex = NUM_SCREENS - 1 - textLength;
|
||||
|
||||
// Insert the pickaxe icon just before the digits
|
||||
if (startIndex > 0)
|
||||
{
|
||||
ret[startIndex - 1] = "mdi:pickaxe";
|
||||
}
|
||||
|
||||
// Place the digits
|
||||
for (std::size_t i = 0; i < textLength; ++i)
|
||||
{
|
||||
ret[startIndex + i] = output.substr(i, 1);
|
||||
}
|
||||
|
||||
ret[NUM_SCREENS - 1] = label;
|
||||
|
||||
if (pool.hasLogo()) {
|
||||
ret[0] = "mdi:miningpool";
|
||||
} else {
|
||||
ret[0] = pool.getDisplayLabel();
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
std::array<std::string, NUM_SCREENS> parseMiningPoolStatsDailyEarnings(int sats, std::string label, const MiningPoolInterface& pool)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> ret;
|
||||
ret.fill(""); // Initialize all elements to empty strings
|
||||
std::string satsDisplay = std::to_string(sats);
|
||||
|
||||
if (sats >= 100000000) {
|
||||
// A whale mining 1+ BTC per day! No decimal points; whales scoff at such things.
|
||||
label = "BTC" + label.substr(4);
|
||||
satsDisplay = satsDisplay.substr(0, satsDisplay.length() - 8);
|
||||
} else if (sats >= 10000000) {
|
||||
// 10.0M to 99.9M you get one decimal point
|
||||
satsDisplay = satsDisplay.substr(0, satsDisplay.length() - 6) + "." + satsDisplay[2] + "M";
|
||||
} else if (sats >= 1000000) {
|
||||
// 1.00M to 9.99M you get two decimal points
|
||||
satsDisplay = satsDisplay.substr(0, satsDisplay.length() - 6) + "." + satsDisplay.substr(2, 2) + "M";
|
||||
} else if (sats >= 100000) {
|
||||
// 100K to 999K you get no extra precision
|
||||
satsDisplay = satsDisplay.substr(0, satsDisplay.length() - 3) + "K";
|
||||
} else if (sats >= 10000) {
|
||||
// 10.0K to 99.9K you get one decimal point
|
||||
satsDisplay = satsDisplay.substr(0, satsDisplay.length() - 3) + "." + satsDisplay[2] + "K";
|
||||
} else {
|
||||
// Pleb miner! 4 digit or fewer sats will fit as-is. no-op.
|
||||
}
|
||||
|
||||
std::size_t textLength = satsDisplay.length();
|
||||
|
||||
// Calculate the position where the digits should start
|
||||
// Account for the position of the mining pool logo
|
||||
std::size_t startIndex = NUM_SCREENS - 1 - textLength;
|
||||
|
||||
// Insert the pickaxe icon just before the digits if there's room
|
||||
if (startIndex > 0)
|
||||
{
|
||||
ret[startIndex - 1] = "mdi:pickaxe";
|
||||
}
|
||||
|
||||
// Place the digits
|
||||
for (std::size_t i = 0; i < textLength; ++i)
|
||||
{
|
||||
ret[startIndex + i] = satsDisplay.substr(i, 1);
|
||||
}
|
||||
|
||||
ret[NUM_SCREENS - 1] = label;
|
||||
|
||||
if (pool.hasLogo()) {
|
||||
ret[0] = "mdi:miningpool";
|
||||
} else {
|
||||
ret[0] = pool.getDisplayLabel();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#include <array>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <utils.hpp>
|
||||
|
||||
#ifndef UNITY_TEST
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#endif
|
||||
|
||||
std::array<std::string, NUM_SCREENS> parseMiningPoolStatsHashRate(const std::string& hashrate, const MiningPoolInterface& pool);
|
||||
std::array<std::string, NUM_SCREENS> parseMiningPoolStatsDailyEarnings(int sats, std::string label, const MiningPoolInterface& pool);
|
|
@ -1,27 +0,0 @@
|
|||
// src/noderunners/noderunners_pool.cpp
|
||||
#include "noderunners_pool.hpp"
|
||||
|
||||
void NoderunnersPool::prepareRequest(HTTPClient& http) const {
|
||||
// Empty as NodeRunners doesn't need special headers
|
||||
}
|
||||
|
||||
std::string NoderunnersPool::getApiUrl() const {
|
||||
return "https://pool.noderunners.network/api/v1/users/" + poolUser;
|
||||
}
|
||||
|
||||
PoolStats NoderunnersPool::parseResponse(const JsonDocument& doc) const {
|
||||
std::string hashrateStr = doc["hashrate1m"].as<std::string>();
|
||||
char unit = hashrateStr.back();
|
||||
std::string value = hashrateStr.substr(0, hashrateStr.size() - 1);
|
||||
|
||||
int multiplier = getHashrateMultiplier(unit);
|
||||
double hashrate = std::stod(value) * std::pow(10, multiplier);
|
||||
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "%.0f", hashrate);
|
||||
|
||||
return PoolStats{
|
||||
.hashrate = buffer,
|
||||
.dailyEarnings = std::nullopt
|
||||
};
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#include <icons/icons.h>
|
||||
#include <utils.hpp>
|
||||
|
||||
class NoderunnersPool : public MiningPoolInterface {
|
||||
public:
|
||||
void setPoolUser(const std::string& user) override { poolUser = user; }
|
||||
|
||||
void prepareRequest(HTTPClient& http) const override;
|
||||
std::string getApiUrl() const override;
|
||||
PoolStats parseResponse(const JsonDocument& doc) const override;
|
||||
bool supportsDailyEarnings() const override { return false; }
|
||||
std::string getDailyEarningsLabel() const override { return ""; }
|
||||
bool hasLogo() const override { return true; }
|
||||
std::string getDisplayLabel() const override { return "NODE/RUNNERS"; } // Fallback if needed
|
||||
std::string getLogoFilename() const override {
|
||||
return "noderunners.bin";
|
||||
}
|
||||
|
||||
std::string getPoolName() const override {
|
||||
return "noderunners";
|
||||
}
|
||||
|
||||
int getLogoWidth() const override {
|
||||
return 122;
|
||||
}
|
||||
|
||||
int getLogoHeight() const override {
|
||||
return 122;
|
||||
}
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
#include "ocean_pool.hpp"
|
||||
|
||||
void OceanPool::prepareRequest(HTTPClient& http) const {
|
||||
// Empty as Ocean doesn't need special headers
|
||||
}
|
||||
|
||||
std::string OceanPool::getApiUrl() const {
|
||||
return "https://api.ocean.xyz/v1/statsnap/" + poolUser;
|
||||
}
|
||||
|
||||
PoolStats OceanPool::parseResponse(const JsonDocument& doc) const {
|
||||
return PoolStats{
|
||||
.hashrate = doc["result"]["hashrate_300s"].as<std::string>(),
|
||||
.dailyEarnings = static_cast<int64_t>(
|
||||
doc["result"]["estimated_earn_next_block"].as<float>() * 100000000
|
||||
)
|
||||
};
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#include <icons/icons.h>
|
||||
|
||||
class OceanPool : public MiningPoolInterface {
|
||||
public:
|
||||
void setPoolUser(const std::string& user) override { poolUser = user; }
|
||||
void prepareRequest(HTTPClient& http) const override;
|
||||
std::string getApiUrl() const override;
|
||||
PoolStats parseResponse(const JsonDocument& doc) const override;
|
||||
bool hasLogo() const override { return true; }
|
||||
std::string getDisplayLabel() const override { return "OCEAN/POOL"; } // Fallback if needed
|
||||
bool supportsDailyEarnings() const override { return true; }
|
||||
std::string getDailyEarningsLabel() const override { return "sats/block"; }
|
||||
std::string getLogoFilename() const override {
|
||||
return "ocean.bin";
|
||||
}
|
||||
|
||||
std::string getPoolName() const override {
|
||||
return "ocean";
|
||||
}
|
||||
|
||||
int getLogoWidth() const override {
|
||||
return 122;
|
||||
}
|
||||
|
||||
int getLogoHeight() const override {
|
||||
return 122;
|
||||
}
|
||||
};
|
|
@ -1,134 +0,0 @@
|
|||
#include "pool_factory.hpp"
|
||||
|
||||
const char* PoolFactory::MINING_POOL_NAME_OCEAN = "ocean";
|
||||
const char* PoolFactory::MINING_POOL_NAME_NODERUNNERS = "noderunners";
|
||||
const char* PoolFactory::MINING_POOL_NAME_BRAIINS = "braiins";
|
||||
const char* PoolFactory::MINING_POOL_NAME_SATOSHI_RADIO = "satoshi_radio";
|
||||
const char* PoolFactory::MINING_POOL_NAME_PUBLIC_POOL = "public_pool";
|
||||
const char* PoolFactory::MINING_POOL_NAME_GOBRRR_POOL = "gobrrr_pool";
|
||||
const char* PoolFactory::LOGOS_DIR = "/logos";
|
||||
|
||||
std::unique_ptr<MiningPoolInterface> PoolFactory::createPool(const std::string& poolName) {
|
||||
static const std::unordered_map<std::string, std::function<std::unique_ptr<MiningPoolInterface>()>> poolFactories = {
|
||||
{MINING_POOL_NAME_OCEAN, []() { return std::make_unique<OceanPool>(); }},
|
||||
{MINING_POOL_NAME_NODERUNNERS, []() { return std::make_unique<NoderunnersPool>(); }},
|
||||
{MINING_POOL_NAME_BRAIINS, []() { return std::make_unique<BraiinsPool>(); }},
|
||||
{MINING_POOL_NAME_SATOSHI_RADIO, []() { return std::make_unique<SatoshiRadioPool>(); }},
|
||||
{MINING_POOL_NAME_PUBLIC_POOL, []() { return std::make_unique<PublicPool>(); }},
|
||||
{MINING_POOL_NAME_GOBRRR_POOL, []() { return std::make_unique<GoBrrrPool>(); }}
|
||||
};
|
||||
|
||||
auto it = poolFactories.find(poolName);
|
||||
if (it == poolFactories.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second();
|
||||
}
|
||||
|
||||
void PoolFactory::downloadPoolLogo(const std::string& poolName, const MiningPoolInterface* poolInterface)
|
||||
{
|
||||
const int MAX_RETRIES = 5;
|
||||
const int RETRY_DELAY_MS = 1000; // 1 second between retries
|
||||
|
||||
if (!poolInterface || !poolInterface->hasLogo()) {
|
||||
Serial.println(F("No pool interface or logo"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure logos directory exists
|
||||
if (!LittleFS.exists(LOGOS_DIR)) {
|
||||
LittleFS.mkdir(LOGOS_DIR);
|
||||
}
|
||||
|
||||
String logoPath = String(LOGOS_DIR) + "/" + String(poolName.c_str()) + "_logo.bin";
|
||||
|
||||
// Only download if the logo doesn't exist
|
||||
if (!LittleFS.exists(logoPath)) {
|
||||
// Clean up logos directory first
|
||||
File root = LittleFS.open(LOGOS_DIR, "r");
|
||||
if (root) {
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
String path = file.path();
|
||||
file.close();
|
||||
LittleFS.remove(path);
|
||||
file = root.openNextFile();
|
||||
}
|
||||
root.close();
|
||||
}
|
||||
|
||||
// Download new logo with retries
|
||||
std::string logoUrl = poolInterface->getLogoUrl();
|
||||
if (!logoUrl.empty()) {
|
||||
for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||
Serial.printf("Downloading pool logo (attempt %d of %d)...\n", attempt, MAX_RETRIES);
|
||||
|
||||
HTTPClient http;
|
||||
http.setUserAgent(USER_AGENT);
|
||||
http.begin(logoUrl.c_str());
|
||||
int httpCode = http.GET();
|
||||
|
||||
if (httpCode == 200) {
|
||||
File file = LittleFS.open(logoPath, "w");
|
||||
if (file) {
|
||||
http.writeToStream(&file);
|
||||
file.close();
|
||||
Serial.println(F("Logo downloaded successfully"));
|
||||
http.end();
|
||||
return; // Success!
|
||||
}
|
||||
}
|
||||
|
||||
http.end();
|
||||
|
||||
if (attempt < MAX_RETRIES) {
|
||||
Serial.printf("Failed to download logo, HTTP code: %d. Retrying...\n", httpCode);
|
||||
vTaskDelay(pdMS_TO_TICKS(RETRY_DELAY_MS));
|
||||
} else {
|
||||
Serial.printf("Failed to download logo after %d attempts\n", MAX_RETRIES);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Serial.println(F("Logo already exists"));
|
||||
}
|
||||
}
|
||||
|
||||
LogoData PoolFactory::loadLogoFromFS(const std::string& poolName, const MiningPoolInterface* poolInterface)
|
||||
{
|
||||
// Initialize with dimensions from the pool interface
|
||||
LogoData logo = {nullptr,
|
||||
0,
|
||||
0,
|
||||
0};
|
||||
|
||||
String logoPath = String(LOGOS_DIR) + "/" + String(poolName.c_str()) + "_logo.bin";
|
||||
if (!LittleFS.exists(logoPath)) {
|
||||
return logo;
|
||||
}
|
||||
|
||||
// Only set dimensions if file exists
|
||||
logo.width = static_cast<size_t>(poolInterface->getLogoWidth());
|
||||
logo.height = static_cast<size_t>(poolInterface->getLogoHeight());
|
||||
|
||||
File file = LittleFS.open(logoPath, "r");
|
||||
if (!file) {
|
||||
return logo;
|
||||
}
|
||||
|
||||
size_t size = file.size();
|
||||
uint8_t* buffer = new uint8_t[size];
|
||||
|
||||
|
||||
if (file.read(buffer, size) == size) {
|
||||
logo.data = buffer;
|
||||
logo.size = size;
|
||||
} else {
|
||||
delete[] buffer;
|
||||
logo.data = nullptr;
|
||||
logo.size = 0;
|
||||
}
|
||||
|
||||
file.close();
|
||||
return logo;
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
#pragma once
|
||||
#include "mining_pool_interface.hpp"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "lib/shared.hpp"
|
||||
#include "lib/config.hpp"
|
||||
|
||||
#include "noderunners/noderunners_pool.hpp"
|
||||
#include "braiins/brains_pool.hpp"
|
||||
#include "ocean/ocean_pool.hpp"
|
||||
#include "satoshi_radio/satoshi_radio_pool.hpp"
|
||||
#include "public_pool/public_pool.hpp"
|
||||
#include "gobrrr_pool/gobrrr_pool.hpp"
|
||||
#include <LittleFS.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
|
||||
class PoolFactory {
|
||||
public:
|
||||
static const char* getLogosDir() { return LOGOS_DIR; }
|
||||
static std::unique_ptr<MiningPoolInterface> createPool(const std::string& poolName);
|
||||
static std::vector<std::string> getAvailablePools() {
|
||||
return {
|
||||
MINING_POOL_NAME_OCEAN,
|
||||
MINING_POOL_NAME_NODERUNNERS,
|
||||
MINING_POOL_NAME_SATOSHI_RADIO,
|
||||
MINING_POOL_NAME_BRAIINS,
|
||||
MINING_POOL_NAME_PUBLIC_POOL,
|
||||
MINING_POOL_NAME_GOBRRR_POOL
|
||||
};
|
||||
}
|
||||
|
||||
static std::string getAvailablePoolsAsString() {
|
||||
const auto pools = getAvailablePools();
|
||||
std::string result;
|
||||
for (size_t i = 0; i < pools.size(); ++i) {
|
||||
result += pools[i];
|
||||
if (i < pools.size() - 1) {
|
||||
result += ", ";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void downloadPoolLogo(const std::string& poolName, const MiningPoolInterface* poolInterface);
|
||||
static LogoData loadLogoFromFS(const std::string& poolName, const MiningPoolInterface* poolInterface);
|
||||
|
||||
private:
|
||||
static const char* MINING_POOL_NAME_OCEAN;
|
||||
static const char* MINING_POOL_NAME_NODERUNNERS;
|
||||
static const char* MINING_POOL_NAME_BRAIINS;
|
||||
static const char* MINING_POOL_NAME_SATOSHI_RADIO;
|
||||
static const char* MINING_POOL_NAME_PUBLIC_POOL;
|
||||
static const char* MINING_POOL_NAME_GOBRRR_POOL;
|
||||
static const char* LOGOS_DIR;
|
||||
};
|
|
@ -1,10 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
struct PoolStats {
|
||||
std::string hashrate;
|
||||
std::optional<int64_t> dailyEarnings;
|
||||
};
|
|
@ -1,21 +0,0 @@
|
|||
// src/noderunners/noderunners_pool.cpp
|
||||
#include "public_pool.hpp"
|
||||
|
||||
std::string PublicPool::getApiUrl() const {
|
||||
return "https://public-pool.io:40557/api/client/" + poolUser;
|
||||
}
|
||||
|
||||
PoolStats PublicPool::parseResponse(const JsonDocument& doc) const {
|
||||
uint64_t totalHashrate = 0;
|
||||
|
||||
for (JsonVariantConst worker : doc["workers"].as<JsonArrayConst>()) {
|
||||
totalHashrate += static_cast<uint64_t>(std::llround(worker["hashRate"].as<double>()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
return PoolStats{
|
||||
.hashrate = std::to_string(totalHashrate),
|
||||
.dailyEarnings = std::nullopt // Public Pool doesn't support daily earnings
|
||||
};
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#include "lib/mining_pool/noderunners/noderunners_pool.hpp"
|
||||
|
||||
#include <icons/icons.h>
|
||||
|
||||
class PublicPool : public NoderunnersPool {
|
||||
public:
|
||||
std::string getApiUrl() const override;
|
||||
bool hasLogo() const override { return false; }
|
||||
std::string getDisplayLabel() const override { return "PUBLIC/POOL"; }
|
||||
PoolStats parseResponse(const JsonDocument& doc) const override;
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
// src/noderunners/noderunners_pool.cpp
|
||||
#include "satoshi_radio_pool.hpp"
|
||||
|
||||
std::string SatoshiRadioPool::getApiUrl() const {
|
||||
return "https://pool.satoshiradio.nl/api/v1/users/" + poolUser;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#include "lib/mining_pool/noderunners/noderunners_pool.hpp"
|
||||
|
||||
#include <icons/icons.h>
|
||||
|
||||
class SatoshiRadioPool : public NoderunnersPool {
|
||||
public:
|
||||
std::string getApiUrl() const override;
|
||||
bool hasLogo() const override { return false; }
|
||||
std::string getDisplayLabel() const override { return "SATOSHI/RADIO"; } // Fallback if needed
|
||||
};
|
|
@ -1,123 +0,0 @@
|
|||
#include "mining_pool_stats_fetch.hpp"
|
||||
|
||||
TaskHandle_t miningPoolStatsFetchTaskHandle;
|
||||
|
||||
std::string miningPoolName;
|
||||
std::string miningPoolStatsHashrate;
|
||||
int miningPoolStatsDailyEarnings;
|
||||
|
||||
std::string getMiningPoolStatsHashRate()
|
||||
{
|
||||
return miningPoolStatsHashrate;
|
||||
}
|
||||
|
||||
int getMiningPoolStatsDailyEarnings()
|
||||
{
|
||||
return miningPoolStatsDailyEarnings;
|
||||
}
|
||||
|
||||
void taskMiningPoolStatsFetch(void *pvParameters)
|
||||
{
|
||||
std::string poolName = preferences.getString("miningPoolName", DEFAULT_MINING_POOL_NAME).c_str();
|
||||
auto poolInterface = PoolFactory::createPool(poolName);
|
||||
|
||||
std::string poolUser = preferences.getString("miningPoolUser", DEFAULT_MINING_POOL_USER).c_str();
|
||||
|
||||
// Main stats fetching loop
|
||||
for (;;)
|
||||
{
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
HTTPClient http;
|
||||
http.setUserAgent(USER_AGENT);
|
||||
|
||||
|
||||
|
||||
poolInterface->setPoolUser(poolUser);
|
||||
std::string apiUrl = poolInterface->getApiUrl();
|
||||
http.begin(apiUrl.c_str());
|
||||
poolInterface->prepareRequest(http);
|
||||
int httpCode = http.GET();
|
||||
if (httpCode == 200)
|
||||
{
|
||||
String payload = http.getString();
|
||||
JsonDocument doc;
|
||||
deserializeJson(doc, payload);
|
||||
|
||||
PoolStats stats = poolInterface->parseResponse(doc);
|
||||
|
||||
miningPoolStatsHashrate = stats.hashrate;
|
||||
|
||||
if (stats.dailyEarnings)
|
||||
{
|
||||
miningPoolStatsDailyEarnings = *stats.dailyEarnings;
|
||||
}
|
||||
else
|
||||
{
|
||||
miningPoolStatsDailyEarnings = 0; // or any other default value
|
||||
}
|
||||
|
||||
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_MINING_POOL_STATS_HASHRATE || getCurrentScreen() == SCREEN_MINING_POOL_STATS_EARNINGS))
|
||||
{
|
||||
WorkItem priceUpdate = {TASK_MINING_POOL_STATS_UPDATE, 0};
|
||||
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.print(
|
||||
F("Error retrieving mining pool data. HTTP status code: "));
|
||||
Serial.println(httpCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void downloadMiningPoolLogoTask(void *pvParameters) {
|
||||
std::string poolName = preferences.getString("miningPoolName", DEFAULT_MINING_POOL_NAME).c_str();
|
||||
auto poolInterface = PoolFactory::createPool(poolName);
|
||||
PoolFactory::downloadPoolLogo(poolName, poolInterface.get());
|
||||
|
||||
// If we're on the mining pool stats screen, trigger a display update
|
||||
if (getCurrentScreen() == SCREEN_MINING_POOL_STATS_HASHRATE) {
|
||||
WorkItem priceUpdate = {TASK_MINING_POOL_STATS_UPDATE, 0};
|
||||
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
||||
}
|
||||
|
||||
xTaskNotifyGive(miningPoolStatsFetchTaskHandle);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void setupMiningPoolStatsFetchTask()
|
||||
{
|
||||
xTaskCreate(downloadMiningPoolLogoTask,
|
||||
"logoDownload",
|
||||
(6 * 1024),
|
||||
NULL,
|
||||
12,
|
||||
NULL);
|
||||
|
||||
xTaskCreate(taskMiningPoolStatsFetch,
|
||||
"miningPoolStatsFetch",
|
||||
(6 * 1024),
|
||||
NULL,
|
||||
tskIDLE_PRIORITY,
|
||||
&miningPoolStatsFetchTaskHandle);
|
||||
}
|
||||
|
||||
std::unique_ptr<MiningPoolInterface>& getMiningPool()
|
||||
{
|
||||
static std::unique_ptr<MiningPoolInterface> currentMiningPool;
|
||||
|
||||
if (!currentMiningPool) {
|
||||
std::string poolName = preferences.getString("miningPoolName", DEFAULT_MINING_POOL_NAME).c_str();
|
||||
currentMiningPool = PoolFactory::createPool(poolName);
|
||||
}
|
||||
|
||||
return currentMiningPool;
|
||||
}
|
||||
|
||||
LogoData getMiningPoolLogo()
|
||||
{
|
||||
LogoData logo = getMiningPool()->getLogo();
|
||||
return logo;
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <HTTPClient.h>
|
||||
#include "mining_pool/pool_factory.hpp"
|
||||
|
||||
#include "lib/config.hpp"
|
||||
#include "lib/shared.hpp"
|
||||
|
||||
extern TaskHandle_t miningPoolStatsFetchTaskHandle;
|
||||
|
||||
void setupMiningPoolStatsFetchTask();
|
||||
void taskMiningPoolStatsFetch(void *pvParameters);
|
||||
|
||||
std::string getMiningPoolStatsHashRate();
|
||||
int getMiningPoolStatsDailyEarnings();
|
||||
|
||||
std::unique_ptr<MiningPoolInterface>& getMiningPool();
|
||||
LogoData getMiningPoolLogo();
|
|
@ -4,20 +4,16 @@ std::vector<nostr::NostrPool *> pools;
|
|||
nostr::Transport *transport;
|
||||
TaskHandle_t nostrTaskHandle = NULL;
|
||||
boolean nostrIsConnected = false;
|
||||
boolean nostrIsSubscribed = false;
|
||||
boolean nostrIsSubscribing = true;
|
||||
|
||||
String subIdZap;
|
||||
|
||||
void setupNostrNotify(bool asDatasource, bool zapNotify)
|
||||
{
|
||||
nostr::esp32::ESP32Platform::initNostr(false);
|
||||
// time_t now;
|
||||
// time(&now);
|
||||
// struct tm *utcTimeInfo;
|
||||
// utcTimeInfo = gmtime(&now);
|
||||
// time_t utcNow = mktime(utcTimeInfo);
|
||||
// time_t timestamp60MinutesAgo = utcNow - 3600;
|
||||
time_t now;
|
||||
time(&now);
|
||||
struct tm *utcTimeInfo;
|
||||
utcTimeInfo = gmtime(&now);
|
||||
time_t utcNow = mktime(utcTimeInfo);
|
||||
time_t timestamp60MinutesAgo = utcNow - 3600;
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -31,7 +27,20 @@ void setupNostrNotify(bool asDatasource, bool zapNotify)
|
|||
|
||||
if (zapNotify)
|
||||
{
|
||||
subscribeZaps(pool, relay, 60);
|
||||
String subIdZap = pool->subscribeMany(
|
||||
{relay},
|
||||
{
|
||||
{
|
||||
{"kinds", {"9735"}},
|
||||
{"limit", {"1"}},
|
||||
{"since", {String(timestamp60MinutesAgo)}},
|
||||
{"#p", {preferences.getString("nostrZapPubkey", DEFAULT_ZAP_NOTIFY_PUBKEY)}},
|
||||
},
|
||||
},
|
||||
handleNostrZapCallback,
|
||||
onNostrSubscriptionClosed,
|
||||
onNostrSubscriptionEose);
|
||||
Serial.println("[ Nostr ] Subscribing to Zap Notifications");
|
||||
}
|
||||
|
||||
if (asDatasource)
|
||||
|
@ -41,7 +50,7 @@ void setupNostrNotify(bool asDatasource, bool zapNotify)
|
|||
{// First filter
|
||||
{
|
||||
{"kinds", {"1"}},
|
||||
{"since", {String(getMinutesAgo(60))}},
|
||||
{"since", {String(timestamp60MinutesAgo)}},
|
||||
{"authors", {pubKey}},
|
||||
}},
|
||||
handleNostrEventCallback,
|
||||
|
@ -63,13 +72,11 @@ void setupNostrNotify(bool asDatasource, bool zapNotify)
|
|||
sstatus="CONNECTED";
|
||||
}else if(status==nostr::ConnectionStatus::DISCONNECTED){
|
||||
nostrIsConnected = false;
|
||||
nostrIsSubscribed = false;
|
||||
sstatus="DISCONNECTED";
|
||||
}else if(status==nostr::ConnectionStatus::ERROR){
|
||||
sstatus = "ERROR";
|
||||
}
|
||||
Serial.println("[ Nostr ] Connection status changed: " + sstatus);
|
||||
});
|
||||
Serial.println("[ Nostr ] Connection status changed: " + sstatus); });
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
|
@ -80,10 +87,8 @@ void setupNostrNotify(bool asDatasource, bool zapNotify)
|
|||
|
||||
void nostrTask(void *pvParameters)
|
||||
{
|
||||
if(preferences.getBool("useNostr", DEFAULT_USE_NOSTR)) {
|
||||
int blockFetch = getBlockFetch();
|
||||
processNewBlock(blockFetch);
|
||||
}
|
||||
int blockFetch = getBlockFetch();
|
||||
processNewBlock(blockFetch);
|
||||
|
||||
while (1)
|
||||
{
|
||||
|
@ -92,10 +97,6 @@ void nostrTask(void *pvParameters)
|
|||
// Run internal loop: refresh relays, complete pending connections, send
|
||||
// pending messages
|
||||
pool->loop();
|
||||
if (!nostrIsSubscribed && !nostrIsSubscribing) {
|
||||
Serial.println(F("Not subscribed"));
|
||||
subscribeZaps(pool, preferences.getString("nostrRelay"), 1);
|
||||
}
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
@ -123,8 +124,6 @@ void onNostrSubscriptionEose(const String &subId)
|
|||
// This is the callback that will be called when the subscription is
|
||||
// EOSE
|
||||
Serial.println("[ Nostr ] Subscription EOSE: " + subId);
|
||||
nostrIsSubscribing = false;
|
||||
nostrIsSubscribed = true;
|
||||
}
|
||||
|
||||
void handleNostrEventCallback(const String &subId, nostr::SignedNostrEvent *event)
|
||||
|
@ -183,34 +182,6 @@ void handleNostrEventCallback(const String &subId, nostr::SignedNostrEvent *even
|
|||
}
|
||||
}
|
||||
|
||||
time_t getMinutesAgo(int min) {
|
||||
time_t now;
|
||||
time(&now);
|
||||
return now - (min * 60);
|
||||
}
|
||||
|
||||
void subscribeZaps(nostr::NostrPool *pool, const String &relay, int minutesAgo) {
|
||||
if (subIdZap) {
|
||||
pool->closeSubscription(subIdZap);
|
||||
}
|
||||
nostrIsSubscribing = true;
|
||||
|
||||
subIdZap = pool->subscribeMany(
|
||||
{relay},
|
||||
{
|
||||
{
|
||||
{"kinds", {"9735"}},
|
||||
{"limit", {"1"}},
|
||||
{"since", {String(getMinutesAgo(minutesAgo))}},
|
||||
{"#p", {preferences.getString("nostrZapPubkey", DEFAULT_ZAP_NOTIFY_PUBKEY)}},
|
||||
},
|
||||
},
|
||||
handleNostrZapCallback,
|
||||
onNostrSubscriptionClosed,
|
||||
onNostrSubscriptionEose);
|
||||
Serial.println("[ Nostr ] Subscribing to Zap Notifications since " + String(getMinutesAgo(minutesAgo)));
|
||||
}
|
||||
|
||||
void handleNostrZapCallback(const String &subId, nostr::SignedNostrEvent *event) {
|
||||
// Received events callback, we can access the event content with
|
||||
// event->getContent() Here you should handle the event, for this
|
||||
|
@ -233,11 +204,9 @@ void handleNostrZapCallback(const String &subId, nostr::SignedNostrEvent *event)
|
|||
|
||||
if (strcmp(key, "bolt11") == 0)
|
||||
{
|
||||
Serial.print(F("Got a zap of "));
|
||||
Serial.println(F("Got a zap"));
|
||||
|
||||
int64_t satsAmount = getAmountInSatoshis(std::string(value));
|
||||
Serial.print(satsAmount);
|
||||
Serial.println(F(" sats"));
|
||||
|
||||
std::array<std::string, NUM_SCREENS> textEpdContent = parseZapNotify(satsAmount, preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL));
|
||||
|
||||
|
@ -252,10 +221,7 @@ void handleNostrZapCallback(const String &subId, nostr::SignedNostrEvent *event)
|
|||
|
||||
setEpdContent(textEpdContent);
|
||||
vTaskDelay(pdMS_TO_TICKS(315 * NUM_SCREENS) + pdMS_TO_TICKS(250));
|
||||
if (preferences.getBool("ledFlashOnZap", DEFAULT_LED_FLASH_ON_ZAP))
|
||||
{
|
||||
queueLedEffect(LED_EFFECT_NOSTR_ZAP);
|
||||
}
|
||||
queueLedEffect(LED_EFFECT_NOSTR_ZAP);
|
||||
if (timerPeriod > 0)
|
||||
{
|
||||
esp_timer_start_periodic(screenRotateTimer,
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
#include "price_notify.hpp"
|
||||
#include "block_notify.hpp"
|
||||
#include "lib/timers.hpp"
|
||||
|
||||
void setupNostrNotify(bool asDatasource, bool zapNotify);
|
||||
|
||||
|
@ -24,7 +23,4 @@ void handleNostrEventCallback(const String &subId, nostr::SignedNostrEvent *even
|
|||
void handleNostrZapCallback(const String &subId, nostr::SignedNostrEvent *event);
|
||||
|
||||
void onNostrSubscriptionClosed(const String &subId, const String &reason);
|
||||
void onNostrSubscriptionEose(const String &subId);
|
||||
|
||||
time_t getMinutesAgo(int min);
|
||||
void subscribeZaps(nostr::NostrPool *pool, const String &relay, int minutesAgo);
|
||||
void onNostrSubscriptionEose(const String &subId);
|
403
src/lib/ota.cpp
403
src/lib/ota.cpp
|
@ -2,14 +2,9 @@
|
|||
|
||||
TaskHandle_t taskOtaHandle = NULL;
|
||||
bool isOtaUpdating = false;
|
||||
QueueHandle_t otaQueue;
|
||||
|
||||
|
||||
|
||||
void setupOTA()
|
||||
{
|
||||
if (preferences.getBool("otaEnabled", DEFAULT_OTA_ENABLED))
|
||||
{
|
||||
void setupOTA() {
|
||||
if (preferences.getBool("otaEnabled", DEFAULT_OTA_ENABLED)) {
|
||||
ArduinoOTA.onStart(onOTAStart);
|
||||
|
||||
ArduinoOTA.onProgress(onOTAProgress);
|
||||
|
@ -21,38 +16,31 @@ void setupOTA()
|
|||
ArduinoOTA.setRebootOnSuccess(false);
|
||||
ArduinoOTA.begin();
|
||||
// downloadUpdate();
|
||||
otaQueue = xQueueCreate(1, sizeof(UpdateMessage));
|
||||
|
||||
xTaskCreate(handleOTATask, "handleOTA", 8192, NULL, 20,
|
||||
xTaskCreate(handleOTATask, "handleOTA", 4096, NULL, tskIDLE_PRIORITY,
|
||||
&taskOtaHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void onOTAProgress(unsigned int progress, unsigned int total)
|
||||
{
|
||||
void onOTAProgress(unsigned int progress, unsigned int total) {
|
||||
uint percentage = progress / (total / 100);
|
||||
pixels.fill(pixels.Color(0, 255, 0));
|
||||
if (percentage < 100)
|
||||
{
|
||||
if (percentage < 100) {
|
||||
pixels.setPixelColor(0, pixels.Color(0, 0, 0));
|
||||
}
|
||||
if (percentage < 75)
|
||||
{
|
||||
if (percentage < 75) {
|
||||
pixels.setPixelColor(1, pixels.Color(0, 0, 0));
|
||||
}
|
||||
if (percentage < 50)
|
||||
{
|
||||
if (percentage < 50) {
|
||||
pixels.setPixelColor(2, pixels.Color(0, 0, 0));
|
||||
}
|
||||
if (percentage < 25)
|
||||
{
|
||||
if (percentage < 25) {
|
||||
pixels.setPixelColor(3, pixels.Color(0, 0, 0));
|
||||
}
|
||||
pixels.show();
|
||||
}
|
||||
|
||||
void onOTAStart()
|
||||
{
|
||||
void onOTAStart() {
|
||||
forceFullRefresh();
|
||||
std::array<String, NUM_SCREENS> epdContent = {"U", "P", "D", "A",
|
||||
"T", "E", "!"};
|
||||
|
@ -67,331 +55,79 @@ void onOTAStart()
|
|||
vTaskSuspend(workerTaskHandle);
|
||||
vTaskSuspend(taskScreenRotateTaskHandle);
|
||||
|
||||
// vTaskSuspend(ledTaskHandle);
|
||||
vTaskSuspend(ledTaskHandle);
|
||||
vTaskSuspend(buttonTaskHandle);
|
||||
|
||||
// stopWebServer();
|
||||
stopWebServer();
|
||||
stopBlockNotify();
|
||||
stopPriceNotify();
|
||||
}
|
||||
|
||||
void handleOTATask(void *parameter)
|
||||
{
|
||||
UpdateMessage msg;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (xQueueReceive(otaQueue, &msg, 0) == pdTRUE)
|
||||
{
|
||||
if (msg.updateType == UPDATE_ALL) {
|
||||
queueLedEffect(LED_FLASH_UPDATE);
|
||||
int resultWebUi = downloadUpdateHandler(UPDATE_WEBUI);
|
||||
queueLedEffect(LED_FLASH_UPDATE);
|
||||
int resultFw = downloadUpdateHandler(UPDATE_FIRMWARE);
|
||||
|
||||
if (resultWebUi == 0 && resultFw == 0) {
|
||||
ESP.restart();
|
||||
} else {
|
||||
queueLedEffect(LED_FLASH_ERROR);
|
||||
vTaskDelay(pdMS_TO_TICKS(3000));
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArduinoOTA.handle(); // Allow OTA updates to occur
|
||||
void handleOTATask(void *parameter) {
|
||||
for (;;) {
|
||||
ArduinoOTA.handle(); // Allow OTA updates to occur
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseInfo getLatestRelease(const String &fileToDownload)
|
||||
{
|
||||
String releaseUrl = preferences.getString("gitReleaseUrl");
|
||||
void downloadUpdate() {
|
||||
WiFiClientSecure client;
|
||||
// client.setCACert(isrg_root_x1cert);
|
||||
client.setCACertBundle(rootca_crt_bundle_start);
|
||||
|
||||
|
||||
client.setInsecure();
|
||||
HTTPClient http;
|
||||
http.begin(client, releaseUrl);
|
||||
http.setUserAgent(USER_AGENT);
|
||||
|
||||
// Send HTTP request to CoinGecko API
|
||||
http.useHTTP10(true);
|
||||
|
||||
http.begin(client,
|
||||
"https://api.github.com/repos/btclock/btclock_v3/releases/latest");
|
||||
int httpCode = http.GET();
|
||||
|
||||
ReleaseInfo info = {"", ""};
|
||||
if (httpCode == 200) {
|
||||
// WiFiClient * stream = http->getStreamPtr();
|
||||
|
||||
if (httpCode > 0)
|
||||
{
|
||||
String payload = http.getString();
|
||||
JsonDocument filter;
|
||||
|
||||
JsonObject filter_assets_0 = filter["assets"].add<JsonObject>();
|
||||
filter_assets_0["name"] = true;
|
||||
filter_assets_0["browser_download_url"] = true;
|
||||
|
||||
JsonDocument doc;
|
||||
deserializeJson(doc, payload);
|
||||
|
||||
JsonArray assets = doc["assets"];
|
||||
DeserializationError error = deserializeJson(
|
||||
doc, http.getStream(), DeserializationOption::Filter(filter));
|
||||
|
||||
for (JsonObject asset : assets)
|
||||
{
|
||||
String assetName = asset["name"].as<String>();
|
||||
if (assetName == fileToDownload)
|
||||
{
|
||||
info.fileUrl = asset["browser_download_url"].as<String>();
|
||||
}
|
||||
else if (assetName == fileToDownload + ".sha256")
|
||||
{
|
||||
info.checksumUrl = asset["browser_download_url"].as<String>();
|
||||
}
|
||||
if (error) {
|
||||
Serial.print("deserializeJson() failed: ");
|
||||
Serial.println(error.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!info.fileUrl.isEmpty() && !info.checksumUrl.isEmpty())
|
||||
{
|
||||
String downloadUrl;
|
||||
for (JsonObject asset : doc["assets"].as<JsonArray>()) {
|
||||
if (asset["name"].as<String>().compareTo("firmware.bin") == 0) {
|
||||
downloadUrl = asset["browser_download_url"].as<String>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
Serial.printf("Latest release URL: %s\r\n", info.fileUrl.c_str());
|
||||
Serial.printf("Checksum URL: %s\r\n", info.checksumUrl.c_str());
|
||||
}
|
||||
http.end();
|
||||
return info;
|
||||
}
|
||||
|
||||
int downloadUpdateHandler(char updateType)
|
||||
{
|
||||
WiFiClientSecure client;
|
||||
client.setCACertBundle(rootca_crt_bundle_start);
|
||||
HTTPClient http;
|
||||
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||
Serial.printf("Download update from %s", downloadUrl);
|
||||
|
||||
ReleaseInfo latestRelease;
|
||||
|
||||
switch (updateType)
|
||||
{
|
||||
case UPDATE_FIRMWARE:
|
||||
{
|
||||
latestRelease = getLatestRelease(getFirmwareFilename());
|
||||
}
|
||||
break;
|
||||
case UPDATE_WEBUI:
|
||||
{
|
||||
latestRelease = getLatestRelease(getWebUiFilename());
|
||||
// updateWebUi(latestRelease.fileUrl, U_SPIFFS);
|
||||
// return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// First, download the expected SHA256
|
||||
String expectedSHA256 = downloadSHA256(latestRelease.checksumUrl);
|
||||
if (expectedSHA256.isEmpty())
|
||||
{
|
||||
Serial.println("Failed to get SHA256 checksum. Aborting update.");
|
||||
return false;
|
||||
}
|
||||
|
||||
http.begin(client, latestRelease.fileUrl);
|
||||
http.setUserAgent(USER_AGENT);
|
||||
|
||||
int httpCode = http.GET();
|
||||
if (httpCode == HTTP_CODE_OK)
|
||||
{
|
||||
int contentLength = http.getSize();
|
||||
if (contentLength > 0)
|
||||
{
|
||||
// Allocate memory to store the firmware
|
||||
uint8_t *firmware = (uint8_t *)malloc(contentLength);
|
||||
if (!firmware)
|
||||
{
|
||||
Serial.println(F("Not enough memory to store firmware"));
|
||||
return false;
|
||||
}
|
||||
|
||||
WiFiClient *stream = http.getStreamPtr();
|
||||
size_t bytesRead = 0;
|
||||
while (bytesRead < contentLength)
|
||||
{
|
||||
size_t available = stream->available();
|
||||
if (available)
|
||||
{
|
||||
size_t readBytes = stream->readBytes(firmware + bytesRead, available);
|
||||
bytesRead += readBytes;
|
||||
}
|
||||
yield(); // Allow background tasks to run
|
||||
}
|
||||
|
||||
if (bytesRead != contentLength)
|
||||
{
|
||||
Serial.println("Failed to read entire firmware");
|
||||
free(firmware);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate SHA256
|
||||
String calculated_sha256 = calculateSHA256(firmware, contentLength);
|
||||
|
||||
Serial.print("Calculated checksum: ");
|
||||
Serial.println(calculated_sha256);
|
||||
Serial.print("Expected checksum: ");
|
||||
Serial.println(expectedSHA256);
|
||||
|
||||
if (calculated_sha256 != expectedSHA256)
|
||||
{
|
||||
Serial.println("Checksum mismatch. Aborting update.");
|
||||
free(firmware);
|
||||
return false;
|
||||
}
|
||||
|
||||
Update.onProgress(onOTAProgress);
|
||||
|
||||
if (Update.begin(contentLength, updateType))
|
||||
{
|
||||
onOTAStart();
|
||||
size_t written = Update.write(firmware, contentLength);
|
||||
|
||||
if (written == contentLength)
|
||||
{
|
||||
Serial.println("Written : " + String(written) + " successfully");
|
||||
free(firmware);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?");
|
||||
free(firmware);
|
||||
return 503;
|
||||
}
|
||||
|
||||
if (Update.end())
|
||||
{
|
||||
Serial.println("OTA done!");
|
||||
if (Update.isFinished())
|
||||
{
|
||||
Serial.println("Update successfully completed. Rebooting.");
|
||||
// ESP.restart();
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Update not finished? Something went wrong!");
|
||||
free(firmware);
|
||||
return 503;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Error Occurred. Error #: " + String(Update.getError()));
|
||||
free(firmware);
|
||||
return 503;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Not enough space to begin OTA");
|
||||
free(firmware);
|
||||
return 503;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Invalid content length");
|
||||
return 503;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.printf("HTTP error: %d\n", httpCode);
|
||||
return 503;
|
||||
}
|
||||
http.end();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void updateWebUi(String latestRelease, int command)
|
||||
{
|
||||
WiFiClientSecure client;
|
||||
client.setCACertBundle(rootca_crt_bundle_start);
|
||||
HTTPClient http;
|
||||
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||
http.begin(client, latestRelease);
|
||||
http.setUserAgent(USER_AGENT);
|
||||
|
||||
int httpCode = http.GET();
|
||||
if (httpCode == HTTP_CODE_OK)
|
||||
{
|
||||
int contentLength = http.getSize();
|
||||
if (contentLength > 0)
|
||||
{
|
||||
uint8_t *buffer = (uint8_t *)malloc(contentLength);
|
||||
if (buffer)
|
||||
{
|
||||
WiFiClient *stream = http.getStreamPtr();
|
||||
size_t written = stream->readBytes(buffer, contentLength);
|
||||
|
||||
if (written == contentLength)
|
||||
{
|
||||
String expectedSHA256 = "";
|
||||
if (command == U_FLASH)
|
||||
{
|
||||
expectedSHA256 = downloadSHA256(getFirmwareFilename());
|
||||
Serial.print("Expected checksum: ");
|
||||
Serial.println(expectedSHA256);
|
||||
}
|
||||
|
||||
String calculated_sha256 = calculateSHA256(buffer, contentLength);
|
||||
Serial.print("Checksum is ");
|
||||
Serial.println(calculated_sha256);
|
||||
if ((command == U_FLASH && expectedSHA256.equals(calculated_sha256)) || command == U_SPIFFS)
|
||||
{
|
||||
Serial.println("Checksum verified. Proceeding with update.");
|
||||
|
||||
Update.onProgress(onOTAProgress);
|
||||
|
||||
if (Update.begin(contentLength, command))
|
||||
{
|
||||
onOTAStart();
|
||||
|
||||
Update.write(buffer, contentLength);
|
||||
if (Update.end())
|
||||
{
|
||||
Serial.println("Update complete. Rebooting.");
|
||||
ESP.restart();
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Error in update process.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Not enough space to begin OTA");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Checksum mismatch. Aborting update.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Error downloading firmware");
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Not enough memory to allocate buffer");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Invalid content length");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.print(httpCode);
|
||||
Serial.println("Error on HTTP request");
|
||||
// esp_http_client_config_t config = {
|
||||
// .url = CONFIG_FIRMWARE_UPGRADE_URL,
|
||||
// };
|
||||
// esp_https_ota_config_t ota_config = {
|
||||
// .http_config = &config,
|
||||
// };
|
||||
// esp_err_t ret = esp_https_ota(&ota_config);
|
||||
// if (ret == ESP_OK)
|
||||
// {
|
||||
// esp_restart();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
void onOTAError(ota_error_t error)
|
||||
{
|
||||
void onOTAError(ota_error_t error) {
|
||||
Serial.println(F("\nOTA update error, restarting"));
|
||||
Wire.end();
|
||||
SPI.end();
|
||||
|
@ -400,8 +136,7 @@ void onOTAError(ota_error_t error)
|
|||
ESP.restart();
|
||||
}
|
||||
|
||||
void onOTAComplete()
|
||||
{
|
||||
void onOTAComplete() {
|
||||
Serial.println(F("\nOTA update finished"));
|
||||
Wire.end();
|
||||
SPI.end();
|
||||
|
@ -409,36 +144,6 @@ void onOTAComplete()
|
|||
ESP.restart();
|
||||
}
|
||||
|
||||
bool getIsOTAUpdating()
|
||||
{
|
||||
bool getIsOTAUpdating() {
|
||||
return isOtaUpdating;
|
||||
}
|
||||
|
||||
String downloadSHA256(const String &sha256Url)
|
||||
{
|
||||
if (sha256Url.isEmpty())
|
||||
{
|
||||
Serial.println("Failed to get SHA256 file URL");
|
||||
return "";
|
||||
}
|
||||
|
||||
WiFiClientSecure client;
|
||||
client.setCACertBundle(rootca_crt_bundle_start);
|
||||
HTTPClient http;
|
||||
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||
http.begin(client, sha256Url);
|
||||
http.setUserAgent(USER_AGENT);
|
||||
|
||||
int httpCode = http.GET();
|
||||
if (httpCode == HTTP_CODE_OK)
|
||||
{
|
||||
String sha256 = http.getString();
|
||||
sha256.trim(); // Remove any whitespace or newline characters
|
||||
return sha256;
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.printf("Failed to download SHA256 file. HTTP error: %d\n", httpCode);
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -1,38 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
#include "lib/config.hpp"
|
||||
#include "lib/shared.hpp"
|
||||
#include "lib/timers.hpp"
|
||||
|
||||
#ifndef UPDATE_MESSAGE_HPP
|
||||
#define UPDATE_MESSAGE_HPP
|
||||
typedef struct {
|
||||
char updateType;
|
||||
} UpdateMessage;
|
||||
#endif
|
||||
|
||||
extern QueueHandle_t otaQueue;
|
||||
|
||||
struct ReleaseInfo {
|
||||
String fileUrl;
|
||||
String checksumUrl;
|
||||
};
|
||||
|
||||
void setupOTA();
|
||||
void onOTAStart();
|
||||
void handleOTATask(void *parameter);
|
||||
void onOTAProgress(unsigned int progress, unsigned int total);
|
||||
// void downloadUpdate();
|
||||
void downloadUpdate();
|
||||
void onOTAError(ota_error_t error);
|
||||
void onOTAComplete();
|
||||
int downloadUpdateHandler(char updateType);
|
||||
ReleaseInfo getLatestRelease(const String& fileToDownload);
|
||||
|
||||
bool getIsOTAUpdating();
|
||||
|
||||
void updateWebUi(String latestRelease, int command);
|
||||
String downloadSHA256(const String& filename);
|
||||
|
||||
bool getIsOTAUpdating();
|
57
src/lib/price_fetch.cpp
Normal file
57
src/lib/price_fetch.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include "price_fetch.hpp"
|
||||
|
||||
const PROGMEM char *cgApiUrl =
|
||||
"https://api.coingecko.com/api/v3/simple/"
|
||||
"price?ids=bitcoin&vs_currencies=usd%2Ceur";
|
||||
|
||||
TaskHandle_t priceFetchTaskHandle;
|
||||
|
||||
void taskPriceFetch(void *pvParameters) {
|
||||
WiFiClientSecure *client = new WiFiClientSecure;
|
||||
client->setInsecure();
|
||||
for (;;) {
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
HTTPClient *http = new HTTPClient();
|
||||
http->setUserAgent(USER_AGENT);
|
||||
|
||||
// Send HTTP request to CoinGecko API
|
||||
http->begin(*client, cgApiUrl);
|
||||
|
||||
int httpCode = http->GET();
|
||||
|
||||
// Parse JSON response and extract average price
|
||||
uint usdPrice, eurPrice;
|
||||
if (httpCode == 200) {
|
||||
String payload = http->getString();
|
||||
JsonDocument doc;
|
||||
deserializeJson(doc, payload);
|
||||
// usdPrice = doc["bitcoin"]["usd"];
|
||||
eurPrice = doc["bitcoin"]["eur"].as<uint>();
|
||||
|
||||
setPrice(eurPrice, CURRENCY_EUR);
|
||||
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER ||
|
||||
getCurrentScreen() == SCREEN_SATS_PER_CURRENCY ||
|
||||
getCurrentScreen() == SCREEN_MARKET_CAP)) {
|
||||
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
|
||||
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
||||
}
|
||||
|
||||
preferences.putUInt("lastPrice", eurPrice);
|
||||
} else {
|
||||
Serial.print(
|
||||
F("Error retrieving BTC/USD price (CoinGecko). HTTP status code: "));
|
||||
Serial.println(httpCode);
|
||||
if (httpCode == -1) {
|
||||
WiFi.reconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setupPriceFetchTask() {
|
||||
xTaskCreate(taskPriceFetch, "priceFetch", (6 * 1024), NULL, tskIDLE_PRIORITY,
|
||||
&priceFetchTaskHandle);
|
||||
|
||||
xTaskNotifyGive(priceFetchTaskHandle);
|
||||
}
|
10
src/lib/price_fetch.hpp
Normal file
10
src/lib/price_fetch.hpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include <Arduino.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#include "lib/config.hpp"
|
||||
#include "lib/shared.hpp"
|
||||
|
||||
extern TaskHandle_t priceFetchTaskHandle;
|
||||
|
||||
void setupPriceFetchTask();
|
||||
void taskPriceFetch(void *pvParameters);
|
|
@ -5,15 +5,15 @@ const char *wsOwnServerV2 = "wss://ws-staging.btclock.dev/api/v2/ws";
|
|||
|
||||
const char *wsServerPrice = "wss://ws.coincap.io/prices?assets=bitcoin";
|
||||
|
||||
|
||||
// WebsocketsClient client;
|
||||
esp_websocket_client_handle_t clientPrice = NULL;
|
||||
esp_websocket_client_config_t config;
|
||||
uint currentPrice = 90000;
|
||||
uint currentPrice = 50000;
|
||||
unsigned long int lastPriceUpdate;
|
||||
bool priceNotifyInit = false;
|
||||
std::map<char, std::uint64_t> currencyMap;
|
||||
std::map<char, unsigned long int> lastUpdateMap;
|
||||
WebSocketsClient priceNotifyWs;
|
||||
|
||||
void setupPriceNotify()
|
||||
{
|
||||
|
@ -26,52 +26,14 @@ void setupPriceNotify()
|
|||
{
|
||||
config = {.uri = wsServerPrice,
|
||||
.user_agent = USER_AGENT};
|
||||
config.cert_pem = isrg_root_x1cert;
|
||||
|
||||
config.task_stack = (6*1024);
|
||||
}
|
||||
|
||||
clientPrice = esp_websocket_client_init(&config);
|
||||
esp_websocket_register_events(clientPrice, WEBSOCKET_EVENT_ANY,
|
||||
onWebsocketPriceEvent, clientPrice);
|
||||
esp_websocket_client_start(clientPrice);
|
||||
|
||||
// priceNotifyWs.beginSSL("ws.coincap.io", 443, "/prices?assets=bitcoin");
|
||||
// priceNotifyWs.onEvent(onWebsocketPriceEvent);
|
||||
// priceNotifyWs.setReconnectInterval(5000);
|
||||
// priceNotifyWs.enableHeartbeat(15000, 3000, 2);
|
||||
}
|
||||
|
||||
|
||||
// void onWebsocketPriceEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||
// switch(type) {
|
||||
// case WStype_DISCONNECTED:
|
||||
// Serial.printf("[WSc] Disconnected!\n");
|
||||
// break;
|
||||
// case WStype_CONNECTED:
|
||||
// {
|
||||
// Serial.printf("[WSc] Connected to url: %s\n", payload);
|
||||
|
||||
|
||||
// break;
|
||||
// }
|
||||
// case WStype_TEXT:
|
||||
// String message = String((char*)payload);
|
||||
// onWebsocketPriceMessage(message);
|
||||
// break;
|
||||
// case WStype_BIN:
|
||||
// break;
|
||||
// case WStype_ERROR:
|
||||
// case WStype_FRAGMENT_TEXT_START:
|
||||
// case WStype_FRAGMENT_BIN_START:
|
||||
// case WStype_FRAGMENT:
|
||||
// case WStype_PING:
|
||||
// case WStype_PONG:
|
||||
// case WStype_FRAGMENT_FIN:
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
|
@ -121,16 +83,16 @@ void processNewPrice(uint newPrice, char currency)
|
|||
"minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE);
|
||||
uint currentTime = esp_timer_get_time() / 1000000;
|
||||
|
||||
if (lastUpdateMap.find(currency) == lastUpdateMap.end() ||
|
||||
if (lastUpdateMap.find(currency) == lastUpdateMap.end()||
|
||||
(currentTime - lastUpdateMap[currency]) > minSecPriceUpd)
|
||||
{
|
||||
// const unsigned long oldPrice = currentPrice;
|
||||
currencyMap[currency] = newPrice;
|
||||
if (currency == CURRENCY_USD && ( lastUpdateMap[currency] == 0 ||
|
||||
(currentTime - lastUpdateMap[currency]) > 120))
|
||||
{
|
||||
preferences.putUInt("lastPrice", currentPrice);
|
||||
}
|
||||
// if (lastUpdateMap[currency] == 0 ||
|
||||
// (currentTime - lastUpdateMap[currency]) > 120)
|
||||
// {
|
||||
// preferences.putUInt("lastPrice", currentPrice);
|
||||
// }
|
||||
lastUpdateMap[currency] = currentTime;
|
||||
// if (abs((int)(oldPrice-currentPrice)) > round(0.0015*oldPrice)) {
|
||||
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER ||
|
||||
|
@ -146,26 +108,22 @@ void processNewPrice(uint newPrice, char currency)
|
|||
|
||||
uint getLastPriceUpdate(char currency)
|
||||
{
|
||||
if (lastUpdateMap.find(currency) == lastUpdateMap.end())
|
||||
{
|
||||
if (lastUpdateMap.find(currency) == lastUpdateMap.end()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return lastUpdateMap[currency];
|
||||
}
|
||||
|
||||
uint getPrice(char currency)
|
||||
{
|
||||
if (currencyMap.find(currency) == currencyMap.end())
|
||||
{
|
||||
uint getPrice(char currency) {
|
||||
if (currencyMap.find(currency) == currencyMap.end()) {
|
||||
return 0;
|
||||
}
|
||||
return currencyMap[currency];
|
||||
return currencyMap[currency];
|
||||
}
|
||||
|
||||
void setPrice(uint newPrice, char currency)
|
||||
{
|
||||
currencyMap[currency] = newPrice;
|
||||
void setPrice(uint newPrice, char currency) {
|
||||
currencyMap[currency] = newPrice;
|
||||
}
|
||||
|
||||
bool isPriceNotifyConnected()
|
||||
|
|
|
@ -12,8 +12,6 @@ void setupPriceNotify();
|
|||
|
||||
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
|
||||
int32_t event_id, void *event_data);
|
||||
//void onWebsocketPriceEvent(WStype_t type, uint8_t * payload, size_t length);
|
||||
|
||||
void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data);
|
||||
|
||||
uint getPrice(char currency);
|
||||
|
|
|
@ -5,15 +5,17 @@
|
|||
// TaskHandle_t timeUpdateTaskHandle;
|
||||
TaskHandle_t taskScreenRotateTaskHandle;
|
||||
TaskHandle_t workerTaskHandle;
|
||||
esp_timer_handle_t screenRotateTimer;
|
||||
esp_timer_handle_t minuteTimer;
|
||||
|
||||
|
||||
std::array<std::string, NUM_SCREENS> taskEpdContent = {};
|
||||
std::array<std::string, NUM_SCREENS> taskEpdContent = {"", "", "", "",
|
||||
"", "", ""};
|
||||
std::string priceString;
|
||||
|
||||
#define WORK_QUEUE_SIZE 10
|
||||
QueueHandle_t workQueue = NULL;
|
||||
|
||||
uint currentScreen = SCREEN_BLOCK_HEIGHT;
|
||||
uint currentScreen;
|
||||
uint currentCurrency = CURRENCY_USD;
|
||||
|
||||
void workerTask(void *pvParameters) {
|
||||
|
@ -22,6 +24,8 @@ void workerTask(void *pvParameters) {
|
|||
while (1) {
|
||||
// Wait for a work item to be available in the queue
|
||||
if (xQueueReceive(workQueue, &receivedItem, portMAX_DELAY)) {
|
||||
uint firstIndex = 0;
|
||||
|
||||
// Process the work item based on its type
|
||||
switch (receivedItem.type) {
|
||||
case TASK_BITAXE_UPDATE: {
|
||||
|
@ -33,28 +37,18 @@ void workerTask(void *pvParameters) {
|
|||
parseBitaxeBestDiff(getBitaxeBestDiff());
|
||||
}
|
||||
setEpdContent(taskEpdContent);
|
||||
break;
|
||||
}
|
||||
case TASK_MINING_POOL_STATS_UPDATE: {
|
||||
if (getCurrentScreen() == SCREEN_MINING_POOL_STATS_HASHRATE) {
|
||||
taskEpdContent =
|
||||
parseMiningPoolStatsHashRate(getMiningPoolStatsHashRate(), *getMiningPool());
|
||||
} else if (getCurrentScreen() == SCREEN_MINING_POOL_STATS_EARNINGS) {
|
||||
taskEpdContent =
|
||||
parseMiningPoolStatsDailyEarnings(getMiningPoolStatsDailyEarnings(), getMiningPool()->getDailyEarningsLabel(), *getMiningPool());
|
||||
}
|
||||
setEpdContent(taskEpdContent);
|
||||
break;
|
||||
|
||||
}
|
||||
break;
|
||||
case TASK_PRICE_UPDATE: {
|
||||
uint currency = getCurrentCurrency();
|
||||
uint price = getPrice(currency);
|
||||
|
||||
// u_char priceSymbol = '$';
|
||||
// if (preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE)) {
|
||||
// priceSymbol = '[';
|
||||
// }
|
||||
if (getCurrentScreen() == SCREEN_BTC_TICKER) {
|
||||
taskEpdContent = parsePriceData(price, currency, preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE),
|
||||
preferences.getBool("mowMode", DEFAULT_MOW_MODE),
|
||||
preferences.getBool("suffixShareDot", DEFAULT_SUFFIX_SHARE_DOT)
|
||||
);
|
||||
taskEpdContent = parsePriceData(price, currency, preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE));
|
||||
} else if (getCurrentScreen() == SCREEN_SATS_PER_CURRENCY) {
|
||||
taskEpdContent = parseSatsPerCurrency(price, currency, preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL));
|
||||
} else {
|
||||
|
@ -128,6 +122,32 @@ void taskScreenRotate(void *pvParameters) {
|
|||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR minuteTimerISR(void *arg) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
// vTaskNotifyGiveFromISR(timeUpdateTaskHandle, &xHigherPriorityTaskWoken);
|
||||
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
|
||||
xQueueSendFromISR(workQueue, &timeUpdate, &xHigherPriorityTaskWoken);
|
||||
if (priceFetchTaskHandle != NULL) {
|
||||
vTaskNotifyGiveFromISR(priceFetchTaskHandle, &xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
if (bitaxeFetchTaskHandle != NULL) {
|
||||
vTaskNotifyGiveFromISR(bitaxeFetchTaskHandle, &xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
if (xHigherPriorityTaskWoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR screenRotateTimerISR(void *arg) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
vTaskNotifyGiveFromISR(taskScreenRotateTaskHandle, &xHigherPriorityTaskWoken);
|
||||
if (xHigherPriorityTaskWoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
void setupTasks() {
|
||||
workQueue = xQueueCreate(WORK_QUEUE_SIZE, sizeof(WorkItem));
|
||||
|
||||
|
@ -138,11 +158,68 @@ void setupTasks() {
|
|||
&taskScreenRotateTaskHandle);
|
||||
|
||||
waitUntilNoneBusy();
|
||||
|
||||
if (findScreenIndexByValue(preferences.getUInt("currentScreen", DEFAULT_CURRENT_SCREEN)) != -1)
|
||||
setCurrentScreen(preferences.getUInt("currentScreen", DEFAULT_CURRENT_SCREEN));
|
||||
setCurrentScreen(preferences.getUInt("currentScreen", DEFAULT_CURRENT_SCREEN));
|
||||
}
|
||||
|
||||
void setupTimeUpdateTimer(void *pvParameters) {
|
||||
const esp_timer_create_args_t minuteTimerConfig = {
|
||||
.callback = &minuteTimerISR, .name = "minute_timer"};
|
||||
|
||||
esp_timer_create(&minuteTimerConfig, &minuteTimer);
|
||||
|
||||
time_t currentTime;
|
||||
struct tm timeinfo;
|
||||
time(¤tTime);
|
||||
localtime_r(¤tTime, &timeinfo);
|
||||
uint32_t secondsUntilNextMinute = 60 - timeinfo.tm_sec;
|
||||
|
||||
if (secondsUntilNextMinute > 0)
|
||||
vTaskDelay(pdMS_TO_TICKS((secondsUntilNextMinute * 1000)));
|
||||
|
||||
esp_timer_start_periodic(minuteTimer, usPerMinute);
|
||||
|
||||
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
|
||||
xQueueSend(workQueue, &timeUpdate, portMAX_DELAY);
|
||||
// xTaskNotifyGive(timeUpdateTaskHandle);
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void setupScreenRotateTimer(void *pvParameters) {
|
||||
const esp_timer_create_args_t screenRotateTimerConfig = {
|
||||
.callback = &screenRotateTimerISR, .name = "screen_rotate_timer"};
|
||||
|
||||
esp_timer_create(&screenRotateTimerConfig, &screenRotateTimer);
|
||||
|
||||
if (preferences.getBool("timerActive", DEFAULT_TIMER_ACTIVE)) {
|
||||
esp_timer_start_periodic(screenRotateTimer,
|
||||
getTimerSeconds() * usPerSecond);
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
uint getTimerSeconds() { return preferences.getUInt("timerSeconds", DEFAULT_TIMER_SECONDS); }
|
||||
|
||||
bool isTimerActive() { return esp_timer_is_active(screenRotateTimer); }
|
||||
|
||||
void setTimerActive(bool status) {
|
||||
if (status) {
|
||||
esp_timer_start_periodic(screenRotateTimer,
|
||||
getTimerSeconds() * usPerSecond);
|
||||
queueLedEffect(LED_EFFECT_START_TIMER);
|
||||
preferences.putBool("timerActive", true);
|
||||
} else {
|
||||
esp_timer_stop(screenRotateTimer);
|
||||
queueLedEffect(LED_EFFECT_PAUSE_TIMER);
|
||||
preferences.putBool("timerActive", false);
|
||||
}
|
||||
|
||||
if (eventSourceTaskHandle != NULL) xTaskNotifyGive(eventSourceTaskHandle);
|
||||
}
|
||||
|
||||
void toggleTimerActive() { setTimerActive(!isTimerActive()); }
|
||||
|
||||
uint getCurrentScreen() { return currentScreen; }
|
||||
|
||||
void setCurrentScreen(uint newScreen) {
|
||||
|
@ -189,17 +266,6 @@ void setCurrentScreen(uint newScreen) {
|
|||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SCREEN_MINING_POOL_STATS_HASHRATE:
|
||||
case SCREEN_MINING_POOL_STATS_EARNINGS: {
|
||||
if (preferences.getBool("miningPoolStats", DEFAULT_MINING_POOL_STATS_ENABLED)) {
|
||||
WorkItem miningPoolStatsUpdate = {TASK_MINING_POOL_STATS_UPDATE, 0};
|
||||
xQueueSend(workQueue, &miningPoolStatsUpdate, portMAX_DELAY);
|
||||
} else {
|
||||
setCurrentScreen(SCREEN_BLOCK_HEIGHT);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
#include <data_handler.hpp>
|
||||
#include <bitaxe_handler.hpp>
|
||||
#include "lib/mining_pool/mining_pool_stats_handler.hpp"
|
||||
|
||||
#include "lib/epd.hpp"
|
||||
#include "lib/price_fetch.hpp"
|
||||
#include "lib/shared.hpp"
|
||||
|
||||
// extern TaskHandle_t priceUpdateTaskHandle;
|
||||
|
@ -17,6 +17,9 @@
|
|||
extern TaskHandle_t workerTaskHandle;
|
||||
extern TaskHandle_t taskScreenRotateTaskHandle;
|
||||
|
||||
extern esp_timer_handle_t screenRotateTimer;
|
||||
extern esp_timer_handle_t minuteTimer;
|
||||
|
||||
extern QueueHandle_t workQueue;
|
||||
|
||||
typedef enum {
|
||||
|
@ -24,8 +27,7 @@ typedef enum {
|
|||
TASK_BLOCK_UPDATE,
|
||||
TASK_FEE_UPDATE,
|
||||
TASK_TIME_UPDATE,
|
||||
TASK_BITAXE_UPDATE,
|
||||
TASK_MINING_POOL_STATS_UPDATE
|
||||
TASK_BITAXE_UPDATE
|
||||
} TaskType;
|
||||
|
||||
typedef struct {
|
||||
|
@ -41,14 +43,21 @@ void previousScreen();
|
|||
|
||||
void showSystemStatusScreen();
|
||||
|
||||
void setupTimeUpdateTimer(void *pvParameters);
|
||||
void setupScreenRotateTimer(void *pvParameters);
|
||||
|
||||
void IRAM_ATTR minuteTimerISR(void *arg);
|
||||
void IRAM_ATTR screenRotateTimerISR(void *arg);
|
||||
|
||||
// void taskPriceUpdate(void *pvParameters);
|
||||
// void taskBlockUpdate(void *pvParameters);
|
||||
// void taskTimeUpdate(void *pvParameters);
|
||||
void taskScreenRotate(void *pvParameters);
|
||||
|
||||
|
||||
uint getTimerSeconds();
|
||||
bool isTimerActive();
|
||||
void setTimerActive(bool status);
|
||||
void toggleTimerActive();
|
||||
|
||||
void setupTasks();
|
||||
void setCurrentCurrency(char currency);
|
||||
|
|
|
@ -1,182 +1,10 @@
|
|||
#include "shared.hpp"
|
||||
|
||||
// const char *github_root_ca =
|
||||
// "-----BEGIN CERTIFICATE-----\n"
|
||||
// "MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL\n"
|
||||
// "MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl\n"
|
||||
// "eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT\n"
|
||||
// "JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx\n"
|
||||
// "MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT\n"
|
||||
// "Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg\n"
|
||||
// "VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm\n"
|
||||
// "aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo\n"
|
||||
// "I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng\n"
|
||||
// "o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G\n"
|
||||
// "A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD\n"
|
||||
// "VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB\n"
|
||||
// "zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW\n"
|
||||
// "RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=\n"
|
||||
// "-----END CERTIFICATE-----\n"
|
||||
// "-----BEGIN CERTIFICATE-----\n"
|
||||
// "MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh\n"
|
||||
// "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
|
||||
// "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\n"
|
||||
// "MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT\n"
|
||||
// "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"
|
||||
// "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG\n"
|
||||
// "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI\n"
|
||||
// "2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx\n"
|
||||
// "1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ\n"
|
||||
// "q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz\n"
|
||||
// "tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ\n"
|
||||
// "vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP\n"
|
||||
// "BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV\n"
|
||||
// "5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY\n"
|
||||
// "1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4\n"
|
||||
// "NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG\n"
|
||||
// "Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91\n"
|
||||
// "8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe\n"
|
||||
// "pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl\n"
|
||||
// "MrY=\n"
|
||||
// "-----END CERTIFICATE-----\n";
|
||||
|
||||
const char* isrg_root_x1cert = R"EOF(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
||||
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
||||
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
||||
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
||||
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
||||
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
||||
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
||||
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
||||
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
||||
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
||||
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
||||
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
||||
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
||||
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
||||
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
||||
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
||||
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
||||
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
||||
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
||||
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
||||
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
||||
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
||||
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
||||
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
||||
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
|
||||
|
||||
#ifdef TEST_SCREENS
|
||||
uint8_t input_buffer[3 * input_buffer_pixels]; // up to depth 24
|
||||
uint8_t output_row_mono_buffer[max_row_width / 8]; // buffer for at least one row of b/w bits
|
||||
uint8_t output_row_color_buffer[max_row_width / 8]; // buffer for at least one row of color bits
|
||||
uint8_t mono_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 b/w
|
||||
uint8_t input_buffer[3 * input_buffer_pixels]; // up to depth 24
|
||||
uint8_t output_row_mono_buffer[max_row_width / 8]; // buffer for at least one row of b/w bits
|
||||
uint8_t output_row_color_buffer[max_row_width / 8]; // buffer for at least one row of color bits
|
||||
uint8_t mono_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 b/w
|
||||
uint8_t color_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 c/w
|
||||
uint16_t rgb_palette_buffer[max_palette_pixels]; // palette buffer for depth <= 8 for buffered graphics, needed for 7-color display
|
||||
#endif
|
||||
|
||||
// Function to calculate SHA-256 hash
|
||||
String calculateSHA256(uint8_t *data, size_t len)
|
||||
{
|
||||
byte shaResult[32];
|
||||
mbedtls_md_context_t ctx;
|
||||
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
|
||||
|
||||
mbedtls_md_init(&ctx);
|
||||
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0);
|
||||
mbedtls_md_starts(&ctx);
|
||||
mbedtls_md_update(&ctx, data, len);
|
||||
mbedtls_md_finish(&ctx, shaResult);
|
||||
mbedtls_md_free(&ctx);
|
||||
|
||||
char sha256_str[65];
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
sprintf(sha256_str + (i * 2), "%02x", shaResult[i]);
|
||||
}
|
||||
sha256_str[64] = 0;
|
||||
|
||||
return String(sha256_str);
|
||||
}
|
||||
|
||||
String calculateSHA256(WiFiClient *stream, size_t contentLength) {
|
||||
mbedtls_md_context_t ctx;
|
||||
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
|
||||
|
||||
mbedtls_md_init(&ctx);
|
||||
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0);
|
||||
mbedtls_md_starts(&ctx);
|
||||
|
||||
uint8_t buff[1024];
|
||||
size_t bytesRead = 0;
|
||||
|
||||
while (bytesRead < contentLength) {
|
||||
size_t toRead = min((size_t)(contentLength - bytesRead), sizeof(buff));
|
||||
size_t readBytes = stream->readBytes(buff, toRead);
|
||||
|
||||
if (readBytes == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
mbedtls_md_update(&ctx, buff, readBytes);
|
||||
bytesRead += readBytes;
|
||||
}
|
||||
|
||||
byte shaResult[32];
|
||||
mbedtls_md_finish(&ctx, shaResult);
|
||||
mbedtls_md_free(&ctx);
|
||||
|
||||
String result = "";
|
||||
for (int i = 0; i < sizeof(shaResult); i++) {
|
||||
char str[3];
|
||||
sprintf(str, "%02x", (int)shaResult[i]);
|
||||
result += str;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// uint8_t* getOceanIcon() {
|
||||
// zlib_turbo zt;
|
||||
// int iUncompSize = zt.gzip_info((uint8_t *)ocean_logo_comp, ocean_logo_size);
|
||||
// uint8_t *pUncompressed;
|
||||
// pUncompressed = (uint8_t *)malloc(iUncompSize+4);
|
||||
// zt.gunzip((uint8_t *)ocean_logo_comp, ocean_logo_size, pUncompressed);
|
||||
// }
|
||||
|
||||
WiFiClientSecure HttpHelper::secureClient;
|
||||
WiFiClient HttpHelper::insecureClient;
|
||||
bool HttpHelper::certBundleSet = false;
|
||||
|
||||
HTTPClient* HttpHelper::begin(const String& url) {
|
||||
HTTPClient* http = new HTTPClient();
|
||||
|
||||
if (url.startsWith("https://")) {
|
||||
if (!certBundleSet) {
|
||||
secureClient.setCACertBundle(rootca_crt_bundle_start);
|
||||
certBundleSet = true;
|
||||
}
|
||||
http->begin(secureClient, url);
|
||||
} else {
|
||||
http->begin(insecureClient, url);
|
||||
}
|
||||
|
||||
http->setUserAgent(USER_AGENT);
|
||||
return http;
|
||||
}
|
||||
|
||||
void HttpHelper::end(HTTPClient* http) {
|
||||
if (http) {
|
||||
http->end();
|
||||
delete http;
|
||||
}
|
||||
}
|
||||
uint16_t rgb_palette_buffer[max_palette_pixels]; // palette buffer for depth <= 8 for buffered graphics, needed for 7-color display
|
||||
#endif
|
|
@ -1,29 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "MCP23017.h"
|
||||
// #include <zlib_turbo.h>
|
||||
#include <Adafruit_MCP23X17.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <Preferences.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <GxEPD2.h>
|
||||
#include <GxEPD2_BW.h>
|
||||
#include <mbedtls/md.h>
|
||||
#include "esp_crt_bundle.h"
|
||||
#include <Update.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <utils.hpp>
|
||||
|
||||
#include "defaults.hpp"
|
||||
|
||||
#define USER_AGENT "BTClock/3.0"
|
||||
|
||||
extern MCP23017 mcp1;
|
||||
#ifdef IS_BTCLOCK_V8
|
||||
extern MCP23017 mcp2;
|
||||
extern Adafruit_MCP23X17 mcp1;
|
||||
#ifdef IS_BTCLOCK_S3
|
||||
extern Adafruit_MCP23X17 mcp2;
|
||||
#endif
|
||||
extern Preferences preferences;
|
||||
extern std::mutex mcpMutex;
|
||||
|
@ -45,16 +37,24 @@ const PROGMEM int SCREEN_BLOCK_FEE_RATE = 6;
|
|||
const PROGMEM int SCREEN_SATS_PER_CURRENCY = 10;
|
||||
|
||||
const PROGMEM int SCREEN_BTC_TICKER = 20;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_USD = 20;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_EUR = 21;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_GBP = 22;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_JPY = 23;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_AUD = 24;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_CAD = 25;
|
||||
|
||||
const PROGMEM int SCREEN_MARKET_CAP = 30;
|
||||
|
||||
const PROGMEM int SCREEN_MINING_POOL_STATS_HASHRATE = 70;
|
||||
const PROGMEM int SCREEN_MINING_POOL_STATS_EARNINGS = 71;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_USD = 30;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_EUR = 31;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_GBP = 32;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_JPY = 33;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_AUD = 34;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_CAD = 35;
|
||||
|
||||
const PROGMEM int SCREEN_BITAXE_HASHRATE = 80;
|
||||
const PROGMEM int SCREEN_BITAXE_BESTDIFF = 81;
|
||||
|
||||
|
||||
const PROGMEM int SCREEN_COUNTDOWN = 98;
|
||||
const PROGMEM int SCREEN_CUSTOM = 99;
|
||||
const int SCREEN_COUNT = 7;
|
||||
|
@ -65,47 +65,7 @@ const PROGMEM int screens[SCREEN_COUNT] = {
|
|||
const int usPerSecond = 1000000;
|
||||
const int usPerMinute = 60 * usPerSecond;
|
||||
|
||||
// extern const char *github_root_ca;
|
||||
extern const char *isrg_root_x1cert;
|
||||
|
||||
extern const uint8_t rootca_crt_bundle_start[] asm("_binary_x509_crt_bundle_start");
|
||||
// extern const uint8_t ocean_logo_comp[] asm("_binary_ocean_gz_start");
|
||||
// extern const uint8_t ocean_logo_comp_end[] asm("_binary_ocean_gz_end");
|
||||
|
||||
// uint8_t* getOceanIcon();
|
||||
|
||||
// const size_t ocean_logo_size = ocean_logo_comp_end - ocean_logo_comp;
|
||||
|
||||
const PROGMEM char UPDATE_FIRMWARE = U_FLASH;
|
||||
const PROGMEM char UPDATE_WEBUI = U_SPIFFS;
|
||||
const PROGMEM char UPDATE_ALL = 99;
|
||||
|
||||
struct ScreenMapping {
|
||||
int value;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
String calculateSHA256(uint8_t* data, size_t len);
|
||||
String calculateSHA256(WiFiClient *stream, size_t contentLength);
|
||||
|
||||
namespace ArduinoJson {
|
||||
template <typename T>
|
||||
struct Converter<std::vector<T>> {
|
||||
static void toJson(const std::vector<T>& src, JsonVariant dst) {
|
||||
JsonArray array = dst.to<JsonArray>();
|
||||
for (T item : src)
|
||||
array.add(item);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class HttpHelper {
|
||||
public:
|
||||
static HTTPClient* begin(const String& url);
|
||||
static void end(HTTPClient* http);
|
||||
|
||||
private:
|
||||
static WiFiClientSecure secureClient;
|
||||
static bool certBundleSet;
|
||||
static WiFiClient insecureClient;
|
||||
};
|
|
@ -1,90 +0,0 @@
|
|||
#include "timers.hpp"
|
||||
|
||||
esp_timer_handle_t screenRotateTimer;
|
||||
esp_timer_handle_t minuteTimer;
|
||||
|
||||
void setupTimeUpdateTimer(void *pvParameters) {
|
||||
const esp_timer_create_args_t minuteTimerConfig = {
|
||||
.callback = &minuteTimerISR, .name = "minute_timer"};
|
||||
|
||||
esp_timer_create(&minuteTimerConfig, &minuteTimer);
|
||||
|
||||
time_t currentTime;
|
||||
struct tm timeinfo;
|
||||
time(¤tTime);
|
||||
localtime_r(¤tTime, &timeinfo);
|
||||
uint32_t secondsUntilNextMinute = 60 - timeinfo.tm_sec;
|
||||
|
||||
if (secondsUntilNextMinute > 0)
|
||||
vTaskDelay(pdMS_TO_TICKS((secondsUntilNextMinute * 1000)));
|
||||
|
||||
esp_timer_start_periodic(minuteTimer, usPerMinute);
|
||||
|
||||
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
|
||||
xQueueSend(workQueue, &timeUpdate, portMAX_DELAY);
|
||||
// xTaskNotifyGive(timeUpdateTaskHandle);
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void setupScreenRotateTimer(void *pvParameters) {
|
||||
const esp_timer_create_args_t screenRotateTimerConfig = {
|
||||
.callback = &screenRotateTimerISR, .name = "screen_rotate_timer"};
|
||||
|
||||
esp_timer_create(&screenRotateTimerConfig, &screenRotateTimer);
|
||||
|
||||
if (preferences.getBool("timerActive", DEFAULT_TIMER_ACTIVE)) {
|
||||
esp_timer_start_periodic(screenRotateTimer,
|
||||
getTimerSeconds() * usPerSecond);
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
uint getTimerSeconds() { return preferences.getUInt("timerSeconds", DEFAULT_TIMER_SECONDS); }
|
||||
|
||||
bool isTimerActive() { return esp_timer_is_active(screenRotateTimer); }
|
||||
|
||||
void setTimerActive(bool status) {
|
||||
if (status) {
|
||||
esp_timer_start_periodic(screenRotateTimer,
|
||||
getTimerSeconds() * usPerSecond);
|
||||
queueLedEffect(LED_EFFECT_START_TIMER);
|
||||
preferences.putBool("timerActive", true);
|
||||
} else {
|
||||
esp_timer_stop(screenRotateTimer);
|
||||
queueLedEffect(LED_EFFECT_PAUSE_TIMER);
|
||||
preferences.putBool("timerActive", false);
|
||||
}
|
||||
|
||||
if (eventSourceTaskHandle != NULL) xTaskNotifyGive(eventSourceTaskHandle);
|
||||
}
|
||||
|
||||
void toggleTimerActive() { setTimerActive(!isTimerActive()); }
|
||||
|
||||
void IRAM_ATTR minuteTimerISR(void *arg) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
// vTaskNotifyGiveFromISR(timeUpdateTaskHandle, &xHigherPriorityTaskWoken);
|
||||
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
|
||||
xQueueSendFromISR(workQueue, &timeUpdate, &xHigherPriorityTaskWoken);
|
||||
|
||||
if (bitaxeFetchTaskHandle != NULL) {
|
||||
vTaskNotifyGiveFromISR(bitaxeFetchTaskHandle, &xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
if (miningPoolStatsFetchTaskHandle != NULL) {
|
||||
vTaskNotifyGiveFromISR(miningPoolStatsFetchTaskHandle, &xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
if (xHigherPriorityTaskWoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR screenRotateTimerISR(void *arg) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
vTaskNotifyGiveFromISR(taskScreenRotateTaskHandle, &xHigherPriorityTaskWoken);
|
||||
if (xHigherPriorityTaskWoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <esp_timer.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include "lib/shared.hpp"
|
||||
#include "lib/screen_handler.hpp"
|
||||
|
||||
extern esp_timer_handle_t screenRotateTimer;
|
||||
extern esp_timer_handle_t minuteTimer;
|
||||
|
||||
void setupTimeUpdateTimer(void *pvParameters);
|
||||
void setupScreenRotateTimer(void *pvParameters);
|
||||
|
||||
void IRAM_ATTR minuteTimerISR(void *arg);
|
||||
void IRAM_ATTR screenRotateTimerISR(void *arg);
|
||||
|
||||
uint getTimerSeconds();
|
||||
bool isTimerActive();
|
||||
void setTimerActive(bool status);
|
||||
void toggleTimerActive();
|
|
@ -1,51 +1,39 @@
|
|||
#include "v2_notify.hpp"
|
||||
|
||||
using namespace V2Notify;
|
||||
WebSocketsClient webSocket;
|
||||
TaskHandle_t v2NotifyTaskHandle;
|
||||
|
||||
namespace V2Notify
|
||||
void setupV2Notify()
|
||||
{
|
||||
WebSocketsClient webSocket;
|
||||
String hostname = "ws.btclock.dev";
|
||||
if ( preferences.getBool("stagingSource", DEFAULT_STAGING_SOURCE)) {
|
||||
Serial.println(F("Connecting to V2 staging source"));
|
||||
hostname = "ws-staging.btclock.dev";
|
||||
}
|
||||
|
||||
TaskHandle_t v2NotifyTaskHandle;
|
||||
webSocket.beginSSL(hostname, 443, "/api/v2/ws");
|
||||
webSocket.onEvent(onWebsocketV2Event);
|
||||
webSocket.setReconnectInterval(5000);
|
||||
webSocket.enableHeartbeat(15000, 3000, 2);
|
||||
|
||||
void setupV2Notify()
|
||||
{
|
||||
String hostname = "ws.btclock.dev";
|
||||
if (preferences.getBool("stagingSource", DEFAULT_STAGING_SOURCE))
|
||||
setupV2NotifyTask();
|
||||
}
|
||||
|
||||
void onWebsocketV2Event(WStype_t type, uint8_t * payload, size_t length) {
|
||||
switch(type) {
|
||||
case WStype_DISCONNECTED:
|
||||
Serial.printf("[WSc] Disconnected!\n");
|
||||
break;
|
||||
case WStype_CONNECTED:
|
||||
{
|
||||
Serial.println(F("Connecting to V2 staging source"));
|
||||
hostname = "ws-staging.btclock.dev";
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println(F("Connecting to V2 source"));
|
||||
}
|
||||
|
||||
webSocket.beginSSL(hostname, 443, "/api/v2/ws");
|
||||
webSocket.onEvent(V2Notify::onWebsocketV2Event);
|
||||
webSocket.setReconnectInterval(5000);
|
||||
webSocket.enableHeartbeat(15000, 3000, 2);
|
||||
|
||||
V2Notify::setupV2NotifyTask();
|
||||
}
|
||||
|
||||
void onWebsocketV2Event(WStype_t type, uint8_t *payload, size_t length)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case WStype_DISCONNECTED:
|
||||
Serial.printf("[WSc] Disconnected!\n");
|
||||
break;
|
||||
case WStype_CONNECTED:
|
||||
{
|
||||
Serial.printf("[WSc] Connected to url: %s\n", payload);
|
||||
Serial.printf("[WSc] Connected to url: %s\n", payload);
|
||||
|
||||
JsonDocument response;
|
||||
|
||||
response["type"] = "subscribe";
|
||||
response["eventType"] = "blockfee";
|
||||
size_t responseLength = measureMsgPack(response);
|
||||
uint8_t *buffer = new uint8_t[responseLength];
|
||||
uint8_t* buffer = new uint8_t[responseLength];
|
||||
serializeMsgPack(response, buffer, responseLength);
|
||||
webSocket.sendBIN(buffer, responseLength);
|
||||
delete[] buffer;
|
||||
|
@ -72,89 +60,83 @@ namespace V2Notify
|
|||
{
|
||||
currenciesArray.add(str);
|
||||
}
|
||||
|
||||
// response["currencies"] = currenciesArray;
|
||||
|
||||
// response["currencies"] = currenciesArray;
|
||||
responseLength = measureMsgPack(response);
|
||||
buffer = new uint8_t[responseLength];
|
||||
serializeMsgPack(response, buffer, responseLength);
|
||||
webSocket.sendBIN(buffer, responseLength);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case WStype_TEXT:
|
||||
Serial.printf("[WSc] get text: %s\n", payload);
|
||||
case WStype_TEXT:
|
||||
Serial.printf("[WSc] get text: %s\n", payload);
|
||||
|
||||
// send message to server
|
||||
// webSocket.sendTXT("message here");
|
||||
break;
|
||||
case WStype_BIN:
|
||||
// send message to server
|
||||
// webSocket.sendTXT("message here");
|
||||
break;
|
||||
case WStype_BIN:
|
||||
{
|
||||
JsonDocument doc;
|
||||
DeserializationError error = deserializeMsgPack(doc, payload, length);
|
||||
|
||||
V2Notify::handleV2Message(doc);
|
||||
break;
|
||||
handleV2Message(doc);
|
||||
break;
|
||||
}
|
||||
case WStype_ERROR:
|
||||
case WStype_FRAGMENT_TEXT_START:
|
||||
case WStype_FRAGMENT_BIN_START:
|
||||
case WStype_FRAGMENT:
|
||||
case WStype_ERROR:
|
||||
case WStype_FRAGMENT_TEXT_START:
|
||||
case WStype_FRAGMENT_BIN_START:
|
||||
case WStype_FRAGMENT:
|
||||
case WStype_PING:
|
||||
case WStype_PONG:
|
||||
case WStype_FRAGMENT_FIN:
|
||||
break;
|
||||
}
|
||||
case WStype_FRAGMENT_FIN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void handleV2Message(JsonDocument doc) {
|
||||
if (doc.containsKey("blockheight"))
|
||||
{
|
||||
uint newBlockHeight = doc["blockheight"].as<uint>();
|
||||
|
||||
if (newBlockHeight == getBlockHeight()) {
|
||||
return;
|
||||
}
|
||||
|
||||
void handleV2Message(JsonDocument doc)
|
||||
{
|
||||
if (doc.containsKey("blockheight"))
|
||||
{
|
||||
uint newBlockHeight = doc["blockheight"].as<uint>();
|
||||
processNewBlock(newBlockHeight);
|
||||
}
|
||||
else if (doc.containsKey("blockfee"))
|
||||
{
|
||||
uint medianFee = doc["blockfee"].as<uint>();
|
||||
|
||||
if (newBlockHeight == getBlockHeight())
|
||||
{
|
||||
return;
|
||||
}
|
||||
processNewBlockFee(medianFee);
|
||||
} else if (doc.containsKey("price"))
|
||||
{
|
||||
|
||||
processNewBlock(newBlockHeight);
|
||||
}
|
||||
else if (doc.containsKey("blockfee"))
|
||||
{
|
||||
uint medianFee = doc["blockfee"].as<uint>();
|
||||
// Iterate through the key-value pairs of the "price" object
|
||||
for (JsonPair kv : doc["price"].as<JsonObject>()) {
|
||||
const char* currency = kv.key().c_str();
|
||||
uint newPrice = kv.value().as<uint>();
|
||||
|
||||
processNewPrice(newPrice, getCurrencyChar(currency));
|
||||
|
||||
processNewBlockFee(medianFee);
|
||||
}
|
||||
else if (doc.containsKey("price"))
|
||||
{
|
||||
|
||||
// Iterate through the key-value pairs of the "price" object
|
||||
for (JsonPair kv : doc["price"].as<JsonObject>())
|
||||
{
|
||||
const char *currency = kv.key().c_str();
|
||||
uint newPrice = kv.value().as<uint>();
|
||||
|
||||
processNewPrice(newPrice, getCurrencyChar(currency));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void taskV2Notify(void *pvParameters)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
webSocket.loop();
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
void taskV2Notify(void *pvParameters) {
|
||||
for(;;) {
|
||||
webSocket.loop();
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void setupV2NotifyTask()
|
||||
{
|
||||
xTaskCreate(V2Notify::taskV2Notify, "v2Notify", (6 * 1024), NULL, tskIDLE_PRIORITY,
|
||||
&V2Notify::v2NotifyTaskHandle);
|
||||
}
|
||||
void setupV2NotifyTask() {
|
||||
xTaskCreate(taskV2Notify, "v2Notify", (6 * 1024), NULL, tskIDLE_PRIORITY,
|
||||
&v2NotifyTaskHandle);
|
||||
|
||||
bool isV2NotifyConnected()
|
||||
{
|
||||
return webSocket.isConnected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isV2NotifyConnected()
|
||||
{
|
||||
return webSocket.isConnected();
|
||||
}
|
||||
|
|
|
@ -8,18 +8,16 @@
|
|||
|
||||
#include "lib/screen_handler.hpp"
|
||||
|
||||
namespace V2Notify {
|
||||
extern TaskHandle_t v2NotifyTaskHandle;
|
||||
extern TaskHandle_t v2NotifyTaskHandle;
|
||||
|
||||
void setupV2NotifyTask();
|
||||
void taskV2Notify(void *pvParameters);
|
||||
void setupV2NotifyTask();
|
||||
void taskV2Notify(void *pvParameters);
|
||||
|
||||
void setupV2Notify();
|
||||
void onWebsocketV2Event(WStype_t type, uint8_t * payload, size_t length);
|
||||
void handleV2Message(JsonDocument doc);
|
||||
void setupV2Notify();
|
||||
void onWebsocketV2Event(WStype_t type, uint8_t * payload, size_t length);
|
||||
void handleV2Message(JsonDocument doc);
|
||||
|
||||
bool isV2NotifyConnected();
|
||||
}
|
||||
bool isV2NotifyConnected();
|
||||
// void stopV2Notify();
|
||||
// void restartV2Notify();
|
||||
// bool getPriceNotifyInit();
|
||||
|
|
|
@ -1,39 +1,24 @@
|
|||
#include "webserver.hpp"
|
||||
|
||||
static const char* JSON_CONTENT = "application/json";
|
||||
|
||||
static const char *const PROGMEM strSettings[] = {
|
||||
"hostnamePrefix", "mempoolInstance", "nostrPubKey", "nostrRelay", "bitaxeHostname", "miningPoolName", "miningPoolUser", "nostrZapPubkey", "httpAuthUser", "httpAuthPass", "gitReleaseUrl", "poolLogosUrl"};
|
||||
|
||||
static const char *const PROGMEM uintSettings[] = {"minSecPriceUpd", "fullRefreshMin", "ledBrightness", "flMaxBrightness", "flEffectDelay", "luxLightToggle", "wpTimeout", "srcV2Currency"};
|
||||
|
||||
static const char *const PROGMEM boolSettings[] = {"fetchEurPrice", "ledTestOnPower", "ledFlashOnUpd",
|
||||
"mdnsEnabled", "otaEnabled", "stealFocus",
|
||||
"mcapBigChar", "useSatsSymbol", "useBlkCountdown",
|
||||
"suffixPrice", "disableLeds", "ownDataSource",
|
||||
"mowMode", "suffixShareDot", "flOffWhenDark",
|
||||
"flAlwaysOn", "flDisable", "flFlashOnUpd",
|
||||
"mempoolSecure", "useNostr", "bitaxeEnabled",
|
||||
"miningPoolStats", "verticalDesc",
|
||||
"nostrZapNotify", "stagingSource", "httpAuthEnabled"};
|
||||
|
||||
AsyncWebServer server(80);
|
||||
AsyncEventSource events("/events");
|
||||
TaskHandle_t eventSourceTaskHandle;
|
||||
|
||||
#define HTTP_OK 200
|
||||
#define HTTP_BAD_REQUEST 400
|
||||
|
||||
void setupWebserver()
|
||||
{
|
||||
events.onConnect([](AsyncEventSourceClient *client)
|
||||
{ client->send("welcome", NULL, millis(), 1000); });
|
||||
server.addHandler(&events);
|
||||
|
||||
AsyncStaticWebHandler &staticHandler = server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html");
|
||||
// server.ad.
|
||||
// server.serveStatic("/css", LittleFS, "/css/");
|
||||
// server.serveStatic("/fonts", LittleFS, "/fonts/");
|
||||
// server.serveStatic("/build", LittleFS, "/build");
|
||||
// server.serveStatic("/swagger.json", LittleFS, "/swagger.json");
|
||||
// server.serveStatic("/api.html", LittleFS, "/api.html");
|
||||
// server.serveStatic("/fs_hash.txt", LittleFS, "/fs_hash.txt");
|
||||
|
||||
server.rewrite("/convert", "/");
|
||||
server.rewrite("/api", "/");
|
||||
AsyncStaticWebHandler &staticHandler = server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html");
|
||||
|
||||
if (preferences.getBool("httpAuthEnabled", DEFAULT_HTTP_AUTH_ENABLED))
|
||||
{
|
||||
|
@ -42,6 +27,7 @@ void setupWebserver()
|
|||
preferences.getString("httpAuthPass", DEFAULT_HTTP_AUTH_PASSWORD));
|
||||
}
|
||||
// server.on("/", HTTP_GET, onIndex);
|
||||
|
||||
server.on("/api/status", HTTP_GET, onApiStatus);
|
||||
server.on("/api/system_status", HTTP_GET, onApiSystemStatus);
|
||||
server.on("/api/wifi_set_tx_power", HTTP_GET, onApiSetWifiTxPower);
|
||||
|
@ -61,8 +47,8 @@ void setupWebserver()
|
|||
|
||||
server.on("/api/show/text", HTTP_GET, onApiShowText);
|
||||
|
||||
server.on("/api/screen/next", HTTP_GET, onApiScreenControl);
|
||||
server.on("/api/screen/previous", HTTP_GET, onApiScreenControl);
|
||||
server.on("/api/screen/next", HTTP_GET, onApiScreenNext);
|
||||
server.on("/api/screen/previous", HTTP_GET, onApiScreenPrevious);
|
||||
|
||||
AsyncCallbackJsonWebHandler *settingsPatchHandler =
|
||||
new AsyncCallbackJsonWebHandler("/api/json/settings", onApiSettingsPatch);
|
||||
|
@ -100,7 +86,6 @@ void setupWebserver()
|
|||
{
|
||||
server.on("/upload/firmware", HTTP_POST, onFirmwareUpdate, asyncFirmwareUpdateHandler);
|
||||
server.on("/upload/webui", HTTP_POST, onFirmwareUpdate, asyncWebuiUpdateHandler);
|
||||
server.on("/api/firmware/auto_update", HTTP_GET, onAutoUpdateFirmware);
|
||||
}
|
||||
|
||||
server.on("/api/restart", HTTP_GET, onApiRestart);
|
||||
|
@ -155,19 +140,6 @@ void onFirmwareUpdate(AsyncWebServerRequest *request)
|
|||
request->send(response);
|
||||
}
|
||||
|
||||
void onAutoUpdateFirmware(AsyncWebServerRequest *request)
|
||||
{
|
||||
UpdateMessage msg = {UPDATE_ALL};
|
||||
if (xQueueSend(otaQueue, &msg, 0) == pdTRUE)
|
||||
{
|
||||
request->send(200, "application/json", "{\"msg\":\"Firmware update triggered\"}");
|
||||
}
|
||||
else
|
||||
{
|
||||
request->send(503,"application/json", "{\"msg\":\"Update already in progress\"}");
|
||||
}
|
||||
}
|
||||
|
||||
void asyncWebuiUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final)
|
||||
{
|
||||
asyncFileUpdateHandler(request, filename, index, data, len, final, U_SPIFFS);
|
||||
|
@ -222,6 +194,36 @@ void asyncFileUpdateHandler(AsyncWebServerRequest *request, String filename, siz
|
|||
void asyncFirmwareUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final)
|
||||
{
|
||||
asyncFileUpdateHandler(request, filename, index, data, len, final, U_FLASH);
|
||||
|
||||
// if (!index)
|
||||
// {
|
||||
// Serial.printf("Update Start: %s\n", filename.c_str());
|
||||
|
||||
// // Update.runAsync(true);
|
||||
// if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000))
|
||||
// {
|
||||
// Update.printError(Serial);
|
||||
// }
|
||||
// }
|
||||
// if (!Update.hasError())
|
||||
// {
|
||||
// if (Update.write(data, len) != len)
|
||||
// {
|
||||
// Update.printError(Serial);
|
||||
// }
|
||||
// }
|
||||
// if (final)
|
||||
// {
|
||||
// if (Update.end(true))
|
||||
// {
|
||||
// Serial.printf("Update Success: %uB\n", index + len);
|
||||
// onApiRestart(request);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Update.printError(Serial);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
JsonDocument getStatusObject()
|
||||
|
@ -240,10 +242,9 @@ JsonDocument getStatusObject()
|
|||
// root["espPsramSize"] = ESP.getPsramSize();
|
||||
|
||||
JsonObject conStatus = root["connectionStatus"].to<JsonObject>();
|
||||
|
||||
conStatus["price"] = isPriceNotifyConnected();
|
||||
conStatus["blocks"] = isBlockNotifyConnected();
|
||||
conStatus["V2"] = V2Notify::isV2NotifyConnected();
|
||||
conStatus["V2"] = isV2NotifyConnected();
|
||||
|
||||
conStatus["nostr"] = nostrConnected();
|
||||
|
||||
|
@ -275,12 +276,12 @@ JsonDocument getLedStatusObject()
|
|||
for (uint i = 0; i < pixels.numPixels(); i++)
|
||||
{
|
||||
uint32_t pixColor = pixels.getPixelColor(pixels.numPixels() - i - 1);
|
||||
uint alpha = (pixColor >> 24) & 0xFF;
|
||||
uint red = (pixColor >> 16) & 0xFF;
|
||||
uint green = (pixColor >> 8) & 0xFF;
|
||||
uint blue = pixColor & 0xFF;
|
||||
char hexColor[8];
|
||||
snprintf(hexColor, sizeof(hexColor), "#%02X%02X%02X", red, green, blue);
|
||||
|
||||
sprintf(hexColor, "#%02X%02X%02X", red, green, blue);
|
||||
|
||||
JsonObject object = colors.add<JsonObject>();
|
||||
object["red"] = red;
|
||||
|
@ -292,26 +293,25 @@ JsonDocument getLedStatusObject()
|
|||
return root;
|
||||
}
|
||||
|
||||
void eventSourceUpdate() {
|
||||
if (!events.count()) return;
|
||||
|
||||
JsonDocument doc = getStatusObject();
|
||||
doc["leds"] = getLedStatusObject()["data"];
|
||||
void eventSourceUpdate()
|
||||
{
|
||||
if (!events.count())
|
||||
return;
|
||||
JsonDocument root = getStatusObject();
|
||||
JsonArray data = root["data"].to<JsonArray>();
|
||||
|
||||
// Get current EPD content directly as array
|
||||
std::array<String, NUM_SCREENS> epdContent = getCurrentEpdContent();
|
||||
|
||||
// Add EPD content arrays
|
||||
JsonArray data = doc["data"].to<JsonArray>();
|
||||
|
||||
// Copy array elements directly
|
||||
for(const auto& content : epdContent) {
|
||||
data.add(content);
|
||||
}
|
||||
root["leds"] = getLedStatusObject()["data"];
|
||||
|
||||
String buffer;
|
||||
serializeJson(doc, buffer);
|
||||
events.send(buffer.c_str(), "status");
|
||||
String epdContent[NUM_SCREENS];
|
||||
std::array<String, NUM_SCREENS> retEpdContent = getCurrentEpdContent();
|
||||
std::copy(std::begin(retEpdContent), std::end(retEpdContent), epdContent);
|
||||
|
||||
copyArray(epdContent, data);
|
||||
|
||||
String bufString;
|
||||
serializeJson(root, bufString);
|
||||
|
||||
events.send(bufString.c_str(), "status");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -321,22 +321,21 @@ void eventSourceUpdate() {
|
|||
void onApiStatus(AsyncWebServerRequest *request)
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
|
||||
JsonDocument root = getStatusObject();
|
||||
|
||||
// Get current EPD content directly as array
|
||||
std::array<String, NUM_SCREENS> epdContent = getCurrentEpdContent();
|
||||
|
||||
// Add EPD content arrays
|
||||
JsonArray data = root["data"].to<JsonArray>();
|
||||
|
||||
// Copy array elements directly
|
||||
for(const auto& content : epdContent) {
|
||||
data.add(content);
|
||||
}
|
||||
JsonArray rendered = root["rendered"].to<JsonArray>();
|
||||
String epdContent[NUM_SCREENS];
|
||||
|
||||
root["leds"] = getLedStatusObject()["data"];
|
||||
|
||||
std::array<String, NUM_SCREENS> retEpdContent = getCurrentEpdContent();
|
||||
|
||||
std::copy(std::begin(retEpdContent), std::end(retEpdContent), epdContent);
|
||||
|
||||
copyArray(epdContent, data);
|
||||
copyArray(epdContent, rendered);
|
||||
serializeJson(root, *response);
|
||||
|
||||
request->send(response);
|
||||
|
@ -349,7 +348,7 @@ void onApiStatus(AsyncWebServerRequest *request)
|
|||
void onApiActionPause(AsyncWebServerRequest *request)
|
||||
{
|
||||
setTimerActive(false);
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -359,7 +358,7 @@ void onApiActionPause(AsyncWebServerRequest *request)
|
|||
void onApiActionTimerRestart(AsyncWebServerRequest *request)
|
||||
{
|
||||
setTimerActive(true);
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -373,7 +372,7 @@ void onApiFullRefresh(AsyncWebServerRequest *request)
|
|||
|
||||
setEpdContent(newEpdContent, true);
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -388,21 +387,28 @@ void onApiShowScreen(AsyncWebServerRequest *request)
|
|||
uint currentScreen = p->value().toInt();
|
||||
setCurrentScreen(currentScreen);
|
||||
}
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Api
|
||||
* @Path("/api/screen/next")
|
||||
*/
|
||||
void onApiScreenControl(AsyncWebServerRequest *request) {
|
||||
const String& action = request->url();
|
||||
if (action.endsWith("/next")) {
|
||||
nextScreen();
|
||||
} else if (action.endsWith("/previous")) {
|
||||
previousScreen();
|
||||
}
|
||||
request->send(HTTP_OK);
|
||||
void onApiScreenNext(AsyncWebServerRequest *request)
|
||||
{
|
||||
nextScreen();
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Api
|
||||
* @Path("/api/screen/previous")
|
||||
*/
|
||||
void onApiScreenPrevious(AsyncWebServerRequest *request)
|
||||
{
|
||||
previousScreen();
|
||||
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onApiShowText(AsyncWebServerRequest *request)
|
||||
|
@ -422,7 +428,7 @@ void onApiShowText(AsyncWebServerRequest *request)
|
|||
setEpdContent(textEpdContent);
|
||||
}
|
||||
setCurrentScreen(SCREEN_CUSTOM);
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onApiShowTextAdvanced(AsyncWebServerRequest *request, JsonVariant &json)
|
||||
|
@ -440,7 +446,7 @@ void onApiShowTextAdvanced(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
setEpdContent(epdContent);
|
||||
|
||||
setCurrentScreen(SCREEN_CUSTOM);
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
||||
|
@ -458,49 +464,49 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
|
||||
bool settingsChanged = true;
|
||||
|
||||
if (settings["fgColor"].is<String>())
|
||||
if (settings.containsKey("fgColor"))
|
||||
{
|
||||
String fgColor = settings["fgColor"].as<String>();
|
||||
uint32_t color = strtol(fgColor.c_str(), NULL, 16);
|
||||
preferences.putUInt("fgColor", color);
|
||||
setFgColor(color);
|
||||
preferences.putUInt("fgColor", strtol(fgColor.c_str(), NULL, 16));
|
||||
setFgColor(int(strtol(fgColor.c_str(), NULL, 16)));
|
||||
Serial.print(F("Setting foreground color to "));
|
||||
Serial.println(color);
|
||||
Serial.println(strtol(fgColor.c_str(), NULL, 16));
|
||||
settingsChanged = true;
|
||||
}
|
||||
if (settings["bgColor"].is<String>())
|
||||
if (settings.containsKey("bgColor"))
|
||||
{
|
||||
String bgColor = settings["bgColor"].as<String>();
|
||||
|
||||
uint32_t color = strtol(bgColor.c_str(), NULL, 16);
|
||||
preferences.putUInt("bgColor", color);
|
||||
setBgColor(color);
|
||||
preferences.putUInt("bgColor", strtol(bgColor.c_str(), NULL, 16));
|
||||
setBgColor(int(strtol(bgColor.c_str(), NULL, 16)));
|
||||
Serial.print(F("Setting background color to "));
|
||||
Serial.println(bgColor.c_str());
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
if (settings["timePerScreen"].is<uint>())
|
||||
if (settings.containsKey("timePerScreen"))
|
||||
{
|
||||
preferences.putUInt("timerSeconds",
|
||||
settings["timePerScreen"].as<uint>() * 60);
|
||||
}
|
||||
|
||||
String strSettings[] = {"hostnamePrefix", "mempoolInstance", "nostrPubKey", "nostrRelay", "bitaxeHostname", "nostrZapPubkey"};
|
||||
|
||||
for (String setting : strSettings)
|
||||
{
|
||||
if (settings[setting].is<String>())
|
||||
if (settings.containsKey(setting))
|
||||
{
|
||||
preferences.putString(setting.c_str(), settings[setting].as<String>());
|
||||
Serial.printf("Setting %s to %s\r\n", setting.c_str(),
|
||||
settings[setting].as<String>().c_str());
|
||||
settings[setting].as<String>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String uintSettings[] = {"minSecPriceUpd", "fullRefreshMin", "ledBrightness", "flMaxBrightness", "flEffectDelay", "luxLightToggle", "wpTimeout", "srcV2Currency"};
|
||||
|
||||
for (String setting : uintSettings)
|
||||
{
|
||||
if (settings[setting].is<uint>())
|
||||
if (settings.containsKey(setting))
|
||||
{
|
||||
preferences.putUInt(setting.c_str(), settings[setting].as<uint>());
|
||||
Serial.printf("Setting %s to %d\r\n", setting.c_str(),
|
||||
|
@ -508,7 +514,7 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
}
|
||||
}
|
||||
|
||||
if (settings["tzOffset"].is<int>())
|
||||
if (settings.containsKey("tzOffset"))
|
||||
{
|
||||
int gmtOffset = settings["tzOffset"].as<int>() * 60;
|
||||
size_t written = preferences.putInt("gmtOffset", gmtOffset);
|
||||
|
@ -516,17 +522,25 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
gmtOffset, settings["tzOffset"].as<int>(), written);
|
||||
}
|
||||
|
||||
String boolSettings[] = {"fetchEurPrice", "ledTestOnPower", "ledFlashOnUpd",
|
||||
"mdnsEnabled", "otaEnabled", "stealFocus",
|
||||
"mcapBigChar", "useSatsSymbol", "useBlkCountdown",
|
||||
"suffixPrice", "disableLeds", "ownDataSource",
|
||||
"flAlwaysOn", "flDisable", "flFlashOnUpd",
|
||||
"mempoolSecure", "useNostr", "bitaxeEnabled",
|
||||
"nostrZapNotify", "stagingSource", "httpAuthEnabled"};
|
||||
|
||||
for (String setting : boolSettings)
|
||||
{
|
||||
if (settings[setting].is<bool>())
|
||||
if (settings.containsKey(setting))
|
||||
{
|
||||
preferences.putBool(setting.c_str(), settings[setting].as<bool>());
|
||||
preferences.putBool(setting.c_str(), settings[setting].as<boolean>());
|
||||
Serial.printf("Setting %s to %d\r\n", setting.c_str(),
|
||||
settings[setting].as<bool>());
|
||||
settings[setting].as<boolean>());
|
||||
}
|
||||
}
|
||||
|
||||
if (settings["screens"].is<JsonArray>())
|
||||
if (settings.containsKey("screens"))
|
||||
{
|
||||
for (JsonVariant screen : settings["screens"].as<JsonArray>())
|
||||
{
|
||||
|
@ -534,12 +548,12 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
uint id = s["id"].as<uint>();
|
||||
String key = "screen[" + String(id) + "]";
|
||||
String prefKey = "screen" + String(id) + "Visible";
|
||||
bool visible = s["enabled"].as<bool>();
|
||||
bool visible = s["enabled"].as<boolean>();
|
||||
preferences.putBool(prefKey.c_str(), visible);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings["actCurrencies"].is<JsonArray>())
|
||||
if (settings.containsKey("actCurrencies"))
|
||||
{
|
||||
String actCurrencies;
|
||||
|
||||
|
@ -553,10 +567,10 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
}
|
||||
|
||||
preferences.putString("actCurrencies", actCurrencies.c_str());
|
||||
Serial.printf("Set actCurrencies: %s\n", actCurrencies.c_str());
|
||||
Serial.printf("Set actCurrencies: %s\n", actCurrencies);
|
||||
}
|
||||
|
||||
if (settings["txPower"].is<int>())
|
||||
if (settings.containsKey("txPower"))
|
||||
{
|
||||
int txPower = settings["txPower"].as<int>();
|
||||
|
||||
|
@ -583,7 +597,7 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
}
|
||||
}
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
if (settingsChanged)
|
||||
{
|
||||
queueLedEffect(LED_FLASH_SUCCESS);
|
||||
|
@ -592,7 +606,7 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
|
||||
void onApiRestart(AsyncWebServerRequest *request)
|
||||
{
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
|
||||
if (events.count())
|
||||
events.send("closing");
|
||||
|
@ -606,7 +620,7 @@ void onApiIdentify(AsyncWebServerRequest *request)
|
|||
{
|
||||
queueLedEffect(LED_FLASH_IDENTIFY);
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -648,15 +662,11 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
|||
root["mcapBigChar"] = preferences.getBool("mcapBigChar", DEFAULT_MCAP_BIG_CHAR);
|
||||
root["mdnsEnabled"] = preferences.getBool("mdnsEnabled", DEFAULT_MDNS_ENABLED);
|
||||
root["otaEnabled"] = preferences.getBool("otaEnabled", DEFAULT_OTA_ENABLED);
|
||||
// root["fetchEurPrice"] = preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE);
|
||||
root["fetchEurPrice"] = preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE);
|
||||
root["useSatsSymbol"] = preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL);
|
||||
root["useBlkCountdown"] = preferences.getBool("useBlkCountdown", DEFAULT_USE_BLOCK_COUNTDOWN);
|
||||
root["suffixPrice"] = preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE);
|
||||
root["disableLeds"] = preferences.getBool("disableLeds", DEFAULT_DISABLE_LEDS);
|
||||
root["mowMode"] = preferences.getBool("mowMode", DEFAULT_MOW_MODE);
|
||||
root["verticalDesc"] = preferences.getBool("verticalDesc", DEFAULT_VERTICAL_DESC);
|
||||
|
||||
root["suffixShareDot"] = preferences.getBool("suffixShareDot", DEFAULT_SUFFIX_SHARE_DOT);
|
||||
|
||||
root["hostnamePrefix"] = preferences.getString("hostnamePrefix", DEFAULT_HOSTNAME_PREFIX);
|
||||
root["hostname"] = getMyHostname();
|
||||
|
@ -671,33 +681,23 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
|||
|
||||
root["nostrZapNotify"] = preferences.getBool("nostrZapNotify", DEFAULT_ZAP_NOTIFY_ENABLED);
|
||||
root["nostrZapPubkey"] = preferences.getString("nostrZapPubkey", DEFAULT_ZAP_NOTIFY_PUBKEY);
|
||||
root["ledFlashOnZap"] = preferences.getBool("ledFlashOnZap", DEFAULT_LED_FLASH_ON_ZAP);
|
||||
|
||||
root["gitReleaseUrl"] = preferences.getString("gitReleaseUrl", DEFAULT_GIT_RELEASE_URL);
|
||||
|
||||
root["bitaxeEnabled"] = preferences.getBool("bitaxeEnabled", DEFAULT_BITAXE_ENABLED);
|
||||
root["bitaxeHostname"] = preferences.getString("bitaxeHostname", DEFAULT_BITAXE_HOSTNAME);
|
||||
|
||||
root["miningPoolStats"] = preferences.getBool("miningPoolStats", DEFAULT_MINING_POOL_STATS_ENABLED);
|
||||
root["miningPoolName"] = preferences.getString("miningPoolName", DEFAULT_MINING_POOL_NAME);
|
||||
root["miningPoolUser"] = preferences.getString("miningPoolUser", DEFAULT_MINING_POOL_USER);
|
||||
root["availablePools"] = PoolFactory::getAvailablePools();
|
||||
root["httpAuthEnabled"] = preferences.getBool("httpAuthEnabled", DEFAULT_HTTP_AUTH_ENABLED);
|
||||
root["httpAuthUser"] = preferences.getString("httpAuthUser", DEFAULT_HTTP_AUTH_USERNAME);
|
||||
root["httpAuthPass"] = preferences.getString("httpAuthPass", DEFAULT_HTTP_AUTH_PASSWORD);
|
||||
|
||||
#ifdef HAS_FRONTLIGHT
|
||||
root["hasFrontlight"] = true;
|
||||
root["flDisable"] = preferences.getBool("flDisable");
|
||||
root["flDisable"] = preferences.getBool("flDisable", DEFAULT_DISABLE_FL);
|
||||
root["flMaxBrightness"] = preferences.getUInt("flMaxBrightness", DEFAULT_FL_MAX_BRIGHTNESS);
|
||||
root["flAlwaysOn"] = preferences.getBool("flAlwaysOn", DEFAULT_FL_ALWAYS_ON);
|
||||
root["flEffectDelay"] = preferences.getUInt("flEffectDelay", DEFAULT_FL_EFFECT_DELAY);
|
||||
root["flFlashOnUpd"] = preferences.getBool("flFlashOnUpd", DEFAULT_FL_FLASH_ON_UPDATE);
|
||||
root["flFlashOnZap"] = preferences.getBool("flFlashOnZap", DEFAULT_FL_FLASH_ON_ZAP);
|
||||
|
||||
root["hasLightLevel"] = hasLightLevel();
|
||||
root["luxLightToggle"] = preferences.getUInt("luxLightToggle", DEFAULT_LUX_LIGHT_TOGGLE);
|
||||
root["flOffWhenDark"] = preferences.getBool("flOffWhenDark", DEFAULT_FL_OFF_WHEN_DARK);
|
||||
|
||||
#else
|
||||
root["hasFrontlight"] = false;
|
||||
root["hasLightLevel"] = false;
|
||||
|
@ -718,8 +718,17 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
|||
#endif
|
||||
JsonArray screens = root["screens"].to<JsonArray>();
|
||||
|
||||
root["actCurrencies"] = getActiveCurrencies();
|
||||
root["availableCurrencies"] = getAvailableCurrencies();
|
||||
JsonArray actCurrencies = root["actCurrencies"].to<JsonArray>();
|
||||
for (const auto &str : getActiveCurrencies())
|
||||
{
|
||||
actCurrencies.add(str);
|
||||
}
|
||||
|
||||
JsonArray availableCurrencies = root["availableCurrencies"].to<JsonArray>();
|
||||
for (const auto &str : getAvailableCurrencies())
|
||||
{
|
||||
availableCurrencies.add(str);
|
||||
}
|
||||
|
||||
std::vector<ScreenMapping> screenNameMap = getScreenNameMap();
|
||||
|
||||
|
@ -732,10 +741,8 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
|||
o["enabled"] = preferences.getBool(key.c_str(), true);
|
||||
}
|
||||
|
||||
root["poolLogosUrl"] = preferences.getString("poolLogosUrl", DEFAULT_MINING_POOL_LOGOS_URL);
|
||||
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
serializeJson(root, *response);
|
||||
|
||||
request->send(response);
|
||||
|
@ -747,9 +754,8 @@ bool processEpdColorSettings(AsyncWebServerRequest *request)
|
|||
if (request->hasParam("fgColor", true))
|
||||
{
|
||||
const AsyncWebParameter *fgColor = request->getParam("fgColor", true);
|
||||
uint32_t color = strtol(fgColor->value().c_str(), NULL, 16);
|
||||
preferences.putUInt("fgColor", color);
|
||||
setFgColor(color);
|
||||
preferences.putUInt("fgColor", strtol(fgColor->value().c_str(), NULL, 16));
|
||||
setFgColor(int(strtol(fgColor->value().c_str(), NULL, 16)));
|
||||
// Serial.print(F("Setting foreground color to "));
|
||||
// Serial.println(fgColor->value().c_str());
|
||||
settingsChanged = true;
|
||||
|
@ -758,9 +764,8 @@ bool processEpdColorSettings(AsyncWebServerRequest *request)
|
|||
{
|
||||
const AsyncWebParameter *bgColor = request->getParam("bgColor", true);
|
||||
|
||||
uint32_t color = strtol(bgColor->value().c_str(), NULL, 16);
|
||||
preferences.putUInt("bgColor", color);
|
||||
setBgColor(color);
|
||||
preferences.putUInt("bgColor", strtol(bgColor->value().c_str(), NULL, 16));
|
||||
setBgColor(int(strtol(bgColor->value().c_str(), NULL, 16)));
|
||||
// Serial.print(F("Setting background color to "));
|
||||
// Serial.println(bgColor->value().c_str());
|
||||
settingsChanged = true;
|
||||
|
@ -772,7 +777,7 @@ bool processEpdColorSettings(AsyncWebServerRequest *request)
|
|||
void onApiSystemStatus(AsyncWebServerRequest *request)
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
|
||||
JsonDocument root;
|
||||
|
||||
|
@ -780,9 +785,6 @@ void onApiSystemStatus(AsyncWebServerRequest *request)
|
|||
root["espHeapSize"] = ESP.getHeapSize();
|
||||
root["espFreePsram"] = ESP.getFreePsram();
|
||||
root["espPsramSize"] = ESP.getPsramSize();
|
||||
root["fsUsedBytes"] = LittleFS.usedBytes();
|
||||
root["fsTotalBytes"] = LittleFS.totalBytes();
|
||||
|
||||
root["rssi"] = WiFi.RSSI();
|
||||
root["txPower"] = WiFi.getTxPower();
|
||||
|
||||
|
@ -814,19 +816,19 @@ void onApiSetWifiTxPower(AsyncWebServerRequest *request)
|
|||
if (WiFi.setTxPower(static_cast<wifi_power_t>(txPower)))
|
||||
{
|
||||
preferences.putInt("txPower", txPower);
|
||||
request->send(HTTP_OK, "application/json", "{\"setTxPower\": \"ok\"}");
|
||||
request->send(200, "application/json", "{\"setTxPower\": \"ok\"}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return request->send(HTTP_BAD_REQUEST);
|
||||
return request->send(400);
|
||||
}
|
||||
|
||||
void onApiLightsStatus(AsyncWebServerRequest *request)
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
|
||||
serializeJson(getLedStatusObject()["data"], *response);
|
||||
|
||||
|
@ -836,7 +838,7 @@ void onApiLightsStatus(AsyncWebServerRequest *request)
|
|||
void onApiStopDataSources(AsyncWebServerRequest *request)
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
|
||||
stopPriceNotify();
|
||||
stopBlockNotify();
|
||||
|
@ -847,7 +849,7 @@ void onApiStopDataSources(AsyncWebServerRequest *request)
|
|||
void onApiRestartDataSources(AsyncWebServerRequest *request)
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
|
||||
restartPriceNotify();
|
||||
restartBlockNotify();
|
||||
|
@ -860,7 +862,7 @@ void onApiRestartDataSources(AsyncWebServerRequest *request)
|
|||
void onApiLightsOff(AsyncWebServerRequest *request)
|
||||
{
|
||||
setLights(0, 0, 0);
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onApiLightsSetColor(AsyncWebServerRequest *request)
|
||||
|
@ -868,7 +870,7 @@ void onApiLightsSetColor(AsyncWebServerRequest *request)
|
|||
if (request->hasParam("c"))
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
|
||||
String rgbColor = request->getParam("c")->value();
|
||||
|
||||
|
@ -892,7 +894,7 @@ void onApiLightsSetColor(AsyncWebServerRequest *request)
|
|||
}
|
||||
else
|
||||
{
|
||||
request->send(HTTP_BAD_REQUEST);
|
||||
request->send(400);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -909,7 +911,7 @@ void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
}
|
||||
|
||||
Serial.printf("Invalid values for LED set %d\n", lights.size());
|
||||
request->send(HTTP_BAD_REQUEST);
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -917,27 +919,27 @@ void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
{
|
||||
unsigned int red, green, blue;
|
||||
|
||||
if (lights[i]["red"].is<uint>() && lights[i]["green"].is<uint>() &&
|
||||
lights[i]["blue"].is<uint>())
|
||||
if (lights[i].containsKey("red") && lights[i].containsKey("green") &&
|
||||
lights[i].containsKey("blue"))
|
||||
{
|
||||
red = lights[i]["red"].as<uint>();
|
||||
green = lights[i]["green"].as<uint>();
|
||||
blue = lights[i]["blue"].as<uint>();
|
||||
}
|
||||
else if (lights[i]["hex"].is<const char*>())
|
||||
else if (lights[i].containsKey("hex"))
|
||||
{
|
||||
if (!sscanf(lights[i]["hex"].as<String>().c_str(), "#%02X%02X%02X", &red,
|
||||
&green, &blue) == 3)
|
||||
{
|
||||
Serial.printf("Invalid hex for LED %d\n", i);
|
||||
request->send(HTTP_BAD_REQUEST);
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.printf("No valid color for LED %d\n", i);
|
||||
request->send(HTTP_BAD_REQUEST);
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -948,7 +950,7 @@ void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
pixels.show();
|
||||
saveLedState();
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onIndex(AsyncWebServerRequest *request)
|
||||
|
@ -958,14 +960,48 @@ void onIndex(AsyncWebServerRequest *request)
|
|||
|
||||
void onNotFound(AsyncWebServerRequest *request)
|
||||
{
|
||||
// Access-Control-Request-Method == POST might be better
|
||||
// Serial.printf("NotFound, URL[%s]\n", request->url());
|
||||
|
||||
// Serial.printf("NotFound, METHOD[%s]\n", request->methodToString());
|
||||
|
||||
// int headers = request->headers();
|
||||
// int i;
|
||||
// for (i = 0; i < headers; i++)
|
||||
// {
|
||||
// AsyncWebHeader *h = request->getHeader(i);
|
||||
// Serial.printf("NotFound HEADER[%s]: %s\n", h->name().c_str(),
|
||||
// h->value().c_str());
|
||||
// }
|
||||
|
||||
// int params = request->params();
|
||||
// for (int i = 0; i < params; i++)
|
||||
// {
|
||||
// const AsyncWebParameter *p = request->getParam(i);
|
||||
// if (p->isFile())
|
||||
// { // p->isPost() is also true
|
||||
// Serial.printf("NotFound FILE[%s]: %s, size: %u\n",
|
||||
// p->name().c_str(), p->value().c_str(), p->size());
|
||||
// }
|
||||
// else if (p->isPost())
|
||||
// {
|
||||
// Serial.printf("NotFound POST[%s]: %s\n", p->name().c_str(),
|
||||
// p->value().c_str());
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Serial.printf("NotFound GET[%s]: %s\n", p->name().c_str(),
|
||||
// p->value().c_str());
|
||||
// }
|
||||
// }
|
||||
|
||||
// Access-Control-Request-Method == POST might be better
|
||||
|
||||
if (request->method() == HTTP_OPTIONS ||
|
||||
request->hasHeader("Sec-Fetch-Mode"))
|
||||
{
|
||||
// Serial.printf("NotFound, Return[%d]\n", 200);
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1001,7 +1037,7 @@ void onApiShowCurrency(AsyncWebServerRequest *request)
|
|||
setCurrentCurrency(curChar);
|
||||
setCurrentScreen(getCurrentScreen());
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
request->send(404);
|
||||
|
@ -1012,13 +1048,13 @@ void onApiFrontlightOn(AsyncWebServerRequest *request)
|
|||
{
|
||||
frontlightFadeInAll();
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onApiFrontlightStatus(AsyncWebServerRequest *request)
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
|
||||
JsonDocument root;
|
||||
|
||||
|
@ -1037,7 +1073,7 @@ void onApiFrontlightFlash(AsyncWebServerRequest *request)
|
|||
{
|
||||
frontlightFlash(preferences.getUInt("flEffectDelay"));
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onApiFrontlightSetBrightness(AsyncWebServerRequest *request)
|
||||
|
@ -1045,11 +1081,11 @@ void onApiFrontlightSetBrightness(AsyncWebServerRequest *request)
|
|||
if (request->hasParam("b"))
|
||||
{
|
||||
frontlightSetBrightness(request->getParam("b")->value().toInt());
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
else
|
||||
{
|
||||
request->send(HTTP_BAD_REQUEST);
|
||||
request->send(400);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1057,6 +1093,6 @@ void onApiFrontlightOff(AsyncWebServerRequest *request)
|
|||
{
|
||||
frontlightFadeOutAll();
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
#endif
|
|
@ -14,7 +14,6 @@
|
|||
#include "lib/price_notify.hpp"
|
||||
#include "lib/screen_handler.hpp"
|
||||
#include "webserver/OneParamRewrite.hpp"
|
||||
#include "lib/mining_pool/pool_factory.hpp"
|
||||
|
||||
extern TaskHandle_t eventSourceTaskHandle;
|
||||
|
||||
|
@ -22,13 +21,13 @@ void stopWebServer();
|
|||
void setupWebserver();
|
||||
bool processEpdColorSettings(AsyncWebServerRequest *request);
|
||||
|
||||
|
||||
|
||||
void onApiStatus(AsyncWebServerRequest *request);
|
||||
void onApiSystemStatus(AsyncWebServerRequest *request);
|
||||
void onApiSetWifiTxPower(AsyncWebServerRequest *request);
|
||||
|
||||
void onApiScreenControl(AsyncWebServerRequest *request);
|
||||
|
||||
void onApiScreenNext(AsyncWebServerRequest *request);
|
||||
void onApiScreenPrevious(AsyncWebServerRequest *request);
|
||||
|
||||
void onApiShowScreen(AsyncWebServerRequest *request);
|
||||
void onApiShowCurrency(AsyncWebServerRequest *request);
|
||||
|
@ -54,7 +53,6 @@ void onFirmwareUpdate(AsyncWebServerRequest *request);
|
|||
void asyncFirmwareUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
||||
void asyncFileUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final, int command);
|
||||
void asyncWebuiUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
||||
void onAutoUpdateFirmware(AsyncWebServerRequest *request);
|
||||
|
||||
void onIndex(AsyncWebServerRequest *request);
|
||||
void onNotFound(AsyncWebServerRequest *request);
|
||||
|
|
|
@ -51,7 +51,7 @@ extern "C" void app_main()
|
|||
if (hasLightLevel()) {
|
||||
if (preferences.getUInt("luxLightToggle", DEFAULT_LUX_LIGHT_TOGGLE) != 0)
|
||||
{
|
||||
if (hasLightLevel() && getLightLevel() <= 1 && preferences.getBool("flOffWhenDark", DEFAULT_FL_OFF_WHEN_DARK))
|
||||
if (hasLightLevel() && getLightLevel() == 0)
|
||||
{
|
||||
if (frontlightIsOn()) {
|
||||
frontlightFadeOutAll();
|
||||
|
|
|
@ -1,310 +1,118 @@
|
|||
#include <data_handler.hpp>
|
||||
#include <unity.h>
|
||||
|
||||
template<size_t N>
|
||||
std::string joinArrayWithBrackets(const std::array<std::string, N>& arr, const std::string& separator = " ") {
|
||||
std::ostringstream result;
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (i > 0) {
|
||||
result << separator;
|
||||
}
|
||||
result << '[' << arr[i] << ']';
|
||||
}
|
||||
return result.str();
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
void setUp(void) {
|
||||
// set stuff up here
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
void tearDown(void) {
|
||||
// clean stuff up here
|
||||
}
|
||||
|
||||
void test_CorrectSatsPerDollarConversion(void)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> output = parseSatsPerCurrency(37253, CURRENCY_USD, false);
|
||||
void test_CorrectSatsPerDollarConversion(void) {
|
||||
std::array<std::string, NUM_SCREENS> output = parseSatsPerCurrency(37253, '$', false);
|
||||
TEST_ASSERT_EQUAL_STRING("MSCW/TIME", output[0].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("2", output[NUM_SCREENS - 4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("6", output[NUM_SCREENS - 3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("8", output[NUM_SCREENS - 2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("4", output[NUM_SCREENS - 1].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("2", output[NUM_SCREENS-4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("6", output[NUM_SCREENS-3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("8", output[NUM_SCREENS-2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("4", output[NUM_SCREENS-1].c_str());
|
||||
}
|
||||
|
||||
void test_SatsPerDollarAfter1B(void)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> output = parseSatsPerCurrency(120000000, CURRENCY_USD, false);
|
||||
TEST_ASSERT_EQUAL_STRING("SATS/USD", output[0].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("0", output[NUM_SCREENS - 5].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING(".", output[NUM_SCREENS - 4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("8", output[NUM_SCREENS - 3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("3", output[NUM_SCREENS - 2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("3", output[NUM_SCREENS - 1].c_str());
|
||||
}
|
||||
|
||||
void test_CorrectSatsPerPoundConversion(void)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> output = parseSatsPerCurrency(37253, CURRENCY_GBP, false);
|
||||
TEST_ASSERT_EQUAL_STRING("SATS/GBP", output[0].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("2", output[NUM_SCREENS - 4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("6", output[NUM_SCREENS - 3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("8", output[NUM_SCREENS - 2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("4", output[NUM_SCREENS - 1].c_str());
|
||||
}
|
||||
|
||||
void test_SixCharacterBlockHeight(void)
|
||||
{
|
||||
void test_SixCharacterBlockHeight(void) {
|
||||
std::array<std::string, NUM_SCREENS> output = parseBlockHeight(999999);
|
||||
TEST_ASSERT_EQUAL_STRING("BLOCK/HEIGHT", output[0].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("9", output[1].c_str());
|
||||
}
|
||||
|
||||
void test_SevenCharacterBlockHeight(void)
|
||||
{
|
||||
void test_SevenCharacterBlockHeight(void) {
|
||||
std::array<std::string, NUM_SCREENS> output = parseBlockHeight(1000000);
|
||||
TEST_ASSERT_EQUAL_STRING("1", output[0].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("0", output[1].c_str());
|
||||
}
|
||||
|
||||
void test_FeeRateDisplay(void)
|
||||
{
|
||||
void test_FeeRateDisplay(void) {
|
||||
uint testValue = 21;
|
||||
std::array<std::string, NUM_SCREENS> output = parseBlockFees(static_cast<std::uint16_t>(testValue));
|
||||
TEST_ASSERT_EQUAL_STRING("FEE/RATE", output[0].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("2", output[NUM_SCREENS - 3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("1", output[NUM_SCREENS - 2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("sat/vB", output[NUM_SCREENS - 1].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("2", output[NUM_SCREENS-3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("1", output[NUM_SCREENS-2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("sat/vB", output[NUM_SCREENS-1].c_str());
|
||||
}
|
||||
|
||||
void test_PriceOf100kusd(void)
|
||||
{
|
||||
|
||||
void test_PriceOf100kusd(void) {
|
||||
std::array<std::string, NUM_SCREENS> output = parsePriceData(100000, '$');
|
||||
TEST_ASSERT_EQUAL_STRING("$", output[0].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("1", output[1].c_str());
|
||||
}
|
||||
|
||||
void test_PriceOf1MillionUsd(void)
|
||||
{
|
||||
void test_PriceOf1MillionUsd(void) {
|
||||
std::array<std::string, NUM_SCREENS> output = parsePriceData(1000000, '$');
|
||||
TEST_ASSERT_EQUAL_STRING("BTC/USD", output[0].c_str());
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("1", output[NUM_SCREENS - 5].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING(".", output[NUM_SCREENS - 4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("0", output[NUM_SCREENS - 3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("0", output[NUM_SCREENS - 2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("M", output[NUM_SCREENS - 1].c_str());
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("1", output[NUM_SCREENS-5].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING(".", output[NUM_SCREENS-4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("0", output[NUM_SCREENS-3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("0", output[NUM_SCREENS-2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("M", output[NUM_SCREENS-1].c_str());
|
||||
}
|
||||
|
||||
void test_PriceSuffixMode(void)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> output = parsePriceData(93000, '$', true, false);
|
||||
TEST_ASSERT_EQUAL_STRING("BTC/USD", output[0].c_str());
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("9", output[NUM_SCREENS - 5].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("3", output[NUM_SCREENS - 4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING(".", output[NUM_SCREENS - 3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("0", output[NUM_SCREENS - 2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("K", output[NUM_SCREENS - 1].c_str());
|
||||
}
|
||||
|
||||
void test_PriceSuffixModeCompact1(void)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> output = parsePriceData(100000, '$', true, false, true);
|
||||
|
||||
std::string joined = joinArrayWithBrackets(output);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("BTC/USD", output[0].c_str(), joined.c_str());
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("$", output[NUM_SCREENS - 6].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("1", output[NUM_SCREENS - 5].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("0", output[NUM_SCREENS - 4].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("0.", output[NUM_SCREENS - 3].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("0", output[NUM_SCREENS - 2].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("K", output[NUM_SCREENS - 1].c_str(), joined.c_str());
|
||||
}
|
||||
|
||||
void test_PriceSuffixModeCompact2(void)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> output = parsePriceData(1000000, '$', true, false, true);
|
||||
|
||||
std::string joined = joinArrayWithBrackets(output);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("BTC/USD", output[0].c_str(), joined.c_str());
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("$", output[NUM_SCREENS - 6].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("1.", output[NUM_SCREENS - 5].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("0", output[NUM_SCREENS - 4].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("0", output[NUM_SCREENS - 3].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("0", output[NUM_SCREENS - 2].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("M", output[NUM_SCREENS - 1].c_str(), joined.c_str());
|
||||
}
|
||||
|
||||
void test_PriceSuffixModeMow(void)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> output = parsePriceData(93600, '$', true, true);
|
||||
|
||||
std::string joined = joinArrayWithBrackets(output);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("$", output[0].c_str(), joined.c_str());
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(".", output[NUM_SCREENS - 5].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("0", output[NUM_SCREENS - 4].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("9", output[NUM_SCREENS - 3].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("3", output[NUM_SCREENS - 2].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("M", output[NUM_SCREENS - 1].c_str(), joined.c_str());
|
||||
}
|
||||
|
||||
void test_PriceSuffixModeMowCompact(void)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> output = parsePriceData(93600, '$', true, true, true);
|
||||
|
||||
std::string joined = joinArrayWithBrackets(output);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("MOW/UNITS", output[0].c_str(), joined.c_str());
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("$", output[NUM_SCREENS - 6].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("0.", output[NUM_SCREENS - 5].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("0", output[NUM_SCREENS - 4].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("9", output[NUM_SCREENS - 3].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("3", output[NUM_SCREENS - 2].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("M", output[NUM_SCREENS - 1].c_str(), joined.c_str());
|
||||
}
|
||||
|
||||
void test_McapLowerUsd(void)
|
||||
{
|
||||
void test_McapLowerUsd(void) {
|
||||
std::array<std::string, NUM_SCREENS> output = parseMarketCap(810000, 26000, '$', true);
|
||||
TEST_ASSERT_EQUAL_STRING("USD/MCAP", output[0].c_str());
|
||||
|
||||
// TEST_ASSERT_EQUAL_STRING("$", output[NUM_SCREENS-6].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("$", output[NUM_SCREENS - 5].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("5", output[NUM_SCREENS - 4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("0", output[NUM_SCREENS - 3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("7", output[NUM_SCREENS - 2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("B", output[NUM_SCREENS - 1].c_str());
|
||||
// TEST_ASSERT_EQUAL_STRING("$", output[NUM_SCREENS-6].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("$", output[NUM_SCREENS-5].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("5", output[NUM_SCREENS-4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("0", output[NUM_SCREENS-3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("7", output[NUM_SCREENS-2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("B", output[NUM_SCREENS-1].c_str());
|
||||
}
|
||||
|
||||
void test_Mcap1TrillionUsd(void)
|
||||
{
|
||||
void test_Mcap1TrillionUsd(void) {
|
||||
std::array<std::string, NUM_SCREENS> output = parseMarketCap(831000, 52000, '$', true);
|
||||
TEST_ASSERT_EQUAL_STRING("USD/MCAP", output[0].c_str());
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("$", output[NUM_SCREENS - 6].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("1", output[NUM_SCREENS - 5].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING(".", output[NUM_SCREENS - 4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("0", output[NUM_SCREENS - 3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("2", output[NUM_SCREENS - 2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("T", output[NUM_SCREENS - 1].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("$", output[NUM_SCREENS-6].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("1", output[NUM_SCREENS-5].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING(".", output[NUM_SCREENS-4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("0", output[NUM_SCREENS-3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("2", output[NUM_SCREENS-2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("T", output[NUM_SCREENS-1].c_str());
|
||||
}
|
||||
|
||||
void test_Mcap1TrillionUsdSmallChars(void)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> output = parseMarketCap(831000, 52000, '$', false);
|
||||
TEST_ASSERT_EQUAL_STRING("USD/MCAP", output[0].c_str());
|
||||
|
||||
std::string joined = joinArrayWithBrackets(output);
|
||||
|
||||
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(" $ ", output[NUM_SCREENS - 6].c_str(), joined.c_str());
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(" 1", output[NUM_SCREENS - 5].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("020", output[NUM_SCREENS - 4].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("825", output[NUM_SCREENS - 3].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("000", output[NUM_SCREENS - 2].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("000", output[NUM_SCREENS - 1].c_str(), joined.c_str());
|
||||
}
|
||||
|
||||
void test_Mcap1TrillionEur(void)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> output = parseMarketCap(831000, 52000, CURRENCY_EUR, true);
|
||||
void test_Mcap1TrillionEur(void) {
|
||||
std::array<std::string, NUM_SCREENS> output = parseMarketCap(831000, 52000, '[', true);
|
||||
TEST_ASSERT_EQUAL_STRING("EUR/MCAP", output[0].c_str());
|
||||
TEST_ASSERT_TRUE(CURRENCY_EUR == output[NUM_SCREENS - 6].c_str()[0]);
|
||||
TEST_ASSERT_EQUAL_STRING("1", output[NUM_SCREENS - 5].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING(".", output[NUM_SCREENS - 4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("0", output[NUM_SCREENS - 3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("2", output[NUM_SCREENS - 2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("T", output[NUM_SCREENS - 1].c_str());
|
||||
}
|
||||
|
||||
void test_Mcap1TrillionEurSmallChars(void)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> output = parseMarketCap(831000, 52000, CURRENCY_EUR, false);
|
||||
TEST_ASSERT_EQUAL_STRING("EUR/MCAP", output[0].c_str());
|
||||
|
||||
std::string joined = joinArrayWithBrackets(output);
|
||||
|
||||
char result[4];
|
||||
snprintf(result, sizeof(result), " %c ", CURRENCY_EUR);
|
||||
TEST_ASSERT_EQUAL_STRING(result, output[NUM_SCREENS - 6].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(" 1", output[NUM_SCREENS - 5].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("020", output[NUM_SCREENS - 4].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("825", output[NUM_SCREENS - 3].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("000", output[NUM_SCREENS - 2].c_str(), joined.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("000", output[NUM_SCREENS - 1].c_str(), joined.c_str());
|
||||
}
|
||||
|
||||
void test_Mcap1TrillionJpy(void)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> output = parseMarketCap(831000, 52000, CURRENCY_JPY, true);
|
||||
TEST_ASSERT_EQUAL_STRING("JPY/MCAP", output[0].c_str());
|
||||
TEST_ASSERT_TRUE(CURRENCY_JPY == output[NUM_SCREENS - 6].c_str()[0]);
|
||||
TEST_ASSERT_EQUAL_STRING("1", output[NUM_SCREENS - 5].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING(".", output[NUM_SCREENS - 4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("0", output[NUM_SCREENS - 3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("2", output[NUM_SCREENS - 2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("T", output[NUM_SCREENS - 1].c_str());
|
||||
}
|
||||
|
||||
void test_Mcap1TrillionJpySmallChars(void)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> output = parseMarketCap(831000, 52000, CURRENCY_JPY, false);
|
||||
TEST_ASSERT_EQUAL_STRING("JPY/MCAP", output[0].c_str());
|
||||
|
||||
char result[4];
|
||||
snprintf(result, sizeof(result), " %c ", CURRENCY_JPY);
|
||||
TEST_ASSERT_EQUAL_STRING(result, output[NUM_SCREENS - 6].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING(" 1", output[NUM_SCREENS - 5].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("020", output[NUM_SCREENS - 4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("825", output[NUM_SCREENS - 3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("000", output[NUM_SCREENS - 2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("000", output[NUM_SCREENS - 1].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("[", output[NUM_SCREENS-6].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("1", output[NUM_SCREENS-5].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING(".", output[NUM_SCREENS-4].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("0", output[NUM_SCREENS-3].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("2", output[NUM_SCREENS-2].c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("T", output[NUM_SCREENS-1].c_str());
|
||||
}
|
||||
|
||||
// not needed when using generate_test_runner.rb
|
||||
int runUnityTests(void)
|
||||
{
|
||||
int runUnityTests(void) {
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_CorrectSatsPerDollarConversion);
|
||||
RUN_TEST(test_CorrectSatsPerPoundConversion);
|
||||
RUN_TEST(test_SatsPerDollarAfter1B);
|
||||
RUN_TEST(test_SixCharacterBlockHeight);
|
||||
RUN_TEST(test_SevenCharacterBlockHeight);
|
||||
RUN_TEST(test_FeeRateDisplay);
|
||||
RUN_TEST(test_PriceOf100kusd);
|
||||
RUN_TEST(test_McapLowerUsd);
|
||||
RUN_TEST(test_Mcap1TrillionUsd);
|
||||
RUN_TEST(test_Mcap1TrillionUsdSmallChars);
|
||||
RUN_TEST(test_Mcap1TrillionEur);
|
||||
RUN_TEST(test_Mcap1TrillionEurSmallChars);
|
||||
RUN_TEST(test_Mcap1TrillionJpy);
|
||||
RUN_TEST(test_Mcap1TrillionJpySmallChars);
|
||||
RUN_TEST(test_PriceSuffixMode);
|
||||
RUN_TEST(test_PriceSuffixModeCompact1);
|
||||
RUN_TEST(test_PriceSuffixModeCompact2);
|
||||
RUN_TEST(test_PriceSuffixModeMow);
|
||||
RUN_TEST(test_PriceSuffixModeMowCompact);
|
||||
//RUN_TEST(test_Mcap1MillionEur);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return runUnityTests();
|
||||
int main(void) {
|
||||
return runUnityTests();
|
||||
}
|
||||
|
||||
extern "C" void app_main()
|
||||
{
|
||||
runUnityTests();
|
||||
extern "C" void app_main() {
|
||||
runUnityTests();
|
||||
}
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
#include <utils.hpp>
|
||||
#include <unity.h>
|
||||
|
||||
void test_parseMiningPoolStatsHashRate1dot34TH(void)
|
||||
{
|
||||
std::string hashrate;
|
||||
std::string label;
|
||||
std::string output;
|
||||
|
||||
parseHashrateString("1340000000000", label, output, 4);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("TH/S", label.c_str(), label.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("1.34", output.c_str(), output.c_str());
|
||||
}
|
||||
|
||||
void test_parseMiningPoolStatsHashRate645GH(void)
|
||||
{
|
||||
std::string hashrate = "645000000000";
|
||||
std::string label;
|
||||
std::string output;
|
||||
|
||||
parseHashrateString(hashrate, label, output, 4);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("GH/S", label.c_str(), label.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("645", output.c_str(), output.c_str());
|
||||
}
|
||||
|
||||
void test_parseMiningPoolStatsHashRateEmpty(void)
|
||||
{
|
||||
std::string hashrate = "";
|
||||
std::string label;
|
||||
std::string output;
|
||||
|
||||
parseHashrateString(hashrate, label, output, 4);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("H/S", label.c_str(), label.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("0", output.c_str(), output.c_str());
|
||||
}
|
||||
|
||||
void test_parseMiningPoolStatsHashRateZero(void)
|
||||
{
|
||||
std::string hashrate = "0";
|
||||
std::string label;
|
||||
std::string output;
|
||||
|
||||
parseHashrateString(hashrate, label, output, 4);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("H/S", label.c_str(), label.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("0", output.c_str(), output.c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// not needed when using generate_test_runner.rb
|
||||
int runUnityTests(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_parseMiningPoolStatsHashRate1dot34TH);
|
||||
RUN_TEST(test_parseMiningPoolStatsHashRate645GH);
|
||||
RUN_TEST(test_parseMiningPoolStatsHashRateZero);
|
||||
RUN_TEST(test_parseMiningPoolStatsHashRateEmpty);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return runUnityTests();
|
||||
}
|
||||
|
||||
extern "C" void app_main()
|
||||
{
|
||||
runUnityTests();
|
||||
}
|
BIN
x509_crt_bundle
BIN
x509_crt_bundle
Binary file not shown.
Loading…
Reference in a new issue