forked from btclock/btclock_v3
Compare commits
127 commits
feature/29
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
957a947bc5 | ||
|
c44626cb42 | ||
|
4fdd6b6b4f | ||
|
8a818c66a0 | ||
|
9889e983ec | ||
|
03dbb8add6 | ||
|
753838b122 | ||
|
46c0f3a22b | ||
|
fb70d435a9 | ||
|
7bcb24bab0 | ||
|
e8a7b221cb | ||
|
aeee5238b3 | ||
|
f613c7e9a1 | ||
c7ea2f3e4d | |||
|
814cd234a9 | ||
f9aa593f0b | |||
01ef6daf9f | |||
8f9307d1e4 | |||
e758659a4a | |||
be224d1f91 | |||
72e5ee6580 | |||
c3af0b4d36 | |||
fabc6c1d28 | |||
e175b5f2f5 | |||
2bc5984f6f | |||
1bd465b33a | |||
|
ae2e6656df | ||
|
c989169ff4 | ||
|
1a4bc9b711 | ||
|
0dcde59fb4 | ||
|
de8fe2e26e | ||
|
af4c466659 | ||
|
83d293c58e | ||
|
da25c7de90 | ||
|
4a52fc0bf2 | ||
db0ec01c86 | |||
|
34b77ea105 | ||
|
dbf2c53083 | ||
|
2a116d97ed | ||
3b6f1db3c5 | |||
9ada991ab1 | |||
132aa835cd | |||
|
d6604d28d6 | ||
|
33c06c86a1 | ||
|
f0f591a16f | ||
|
41b5fcf1c1 | ||
|
981895d315 | ||
|
031b506fed | ||
|
4cda081d05 | ||
|
2951055f68 | ||
|
d37307cccf | ||
|
239297c26d | ||
|
3b47c81cfe | ||
|
9f3351f85b | ||
|
1ccd5f18fb | ||
|
82dd70a38d | ||
|
a614bd15db | ||
|
b0ec0685a1 | ||
|
85579e98cf | ||
|
e8a9e253f7 | ||
|
8b72f2f6b3 | ||
|
ff0d8f5a0a | ||
|
41bf2480ce | ||
|
ff50acf913 | ||
|
5dd47c2275 | ||
|
630943ec54 | ||
|
18bac7dcc7 | ||
|
023ff29131 | ||
|
d00c216126 | ||
|
283469dc4c | ||
|
5d5b09f56c | ||
|
1f2110fc5a | ||
|
5425ea7fbf | ||
|
7a1ce54248 | ||
|
f42cd250fe | ||
|
849e5ce439 | ||
|
c276d32807 | ||
|
a4ff5a2f75 | ||
|
00ac808731 | ||
|
6cf464c3e3 | ||
|
a31a42511f | ||
|
478c951ffb | ||
|
9c67f769d3 | ||
|
65496fbb29 | ||
|
2777637355 | ||
|
e39a0ccc14 | ||
|
99e622eeef | ||
|
0a08c5f9ea | ||
|
b13c7242a6 | ||
|
ca1c7178f1 | ||
|
19559727c9 | ||
|
60593de785 | ||
|
87b22e5851 | ||
|
8e71f29d10 | ||
|
1d710ba7f7 | ||
|
4f4e37ec3c | ||
|
ac02e1470d | ||
|
fb67893f85 | ||
|
a9489c30f6 | ||
|
24c3b46365 | ||
|
262eae22dc | ||
|
a8baa085c7 | ||
|
08929eb552 | ||
|
2a8e391342 | ||
|
313efb7604 | ||
|
491618dd78 | ||
|
474ddbb086 | ||
|
9ede0f4dc3 | ||
|
32e40e2cb7 | ||
|
82e80f66e2 | ||
|
f7599cb0ff | ||
|
88615ce248 | ||
|
ba0594959e | ||
|
db1523bef1 | ||
|
18139a9907 | ||
|
e008383ab1 | ||
|
858241bd57 | ||
|
ef7d629e8c | ||
|
9cdbcc6046 | ||
|
d4a05e2f36 | ||
|
e0283d98ca | ||
|
efaab00fb4 | ||
|
a2fa0a12a8 | ||
|
4da04ca3ee | ||
|
ad0800c233 | ||
|
2ef56c1938 | ||
|
91fc474a1f |
99 changed files with 8797 additions and 9638 deletions
190
.forgejo/workflows/push.yaml
Normal file
190
.forgejo/workflows/push.yaml
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
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
|
4
.github/actions/install-build/action.yml
vendored
4
.github/actions/install-build/action.yml
vendored
|
@ -9,14 +9,14 @@ runs:
|
||||||
node-version: lts/*
|
node-version: lts/*
|
||||||
cache: yarn
|
cache: yarn
|
||||||
cache-dependency-path: '**/yarn.lock'
|
cache-dependency-path: '**/yarn.lock'
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/pip
|
~/.cache/pip
|
||||||
~/.platformio/.cache
|
~/.platformio/.cache
|
||||||
~/data/node_modules
|
~/data/node_modules
|
||||||
key: ${{ runner.os }}-pio
|
key: ${{ runner.os }}-pio
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
python-version: '3.9'
|
||||||
- name: Get current date
|
- name: Get current date
|
||||||
|
|
59
.github/workflows/tagging.yml
vendored
59
.github/workflows/tagging.yml
vendored
|
@ -3,7 +3,7 @@ name: BTClock CI
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- "*"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -22,6 +22,7 @@ jobs:
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
include-hidden-files: true
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
name: prepared-outputs
|
name: prepared-outputs
|
||||||
path: .pio/**/*.bin
|
path: .pio/**/*.bin
|
||||||
|
@ -37,7 +38,16 @@ jobs:
|
||||||
chip:
|
chip:
|
||||||
- name: lolin_s3_mini
|
- name: lolin_s3_mini
|
||||||
version: esp32s3
|
version: esp32s3
|
||||||
|
- name: btclock_rev_b
|
||||||
|
version: esp32s3
|
||||||
|
- name: btclock_v8
|
||||||
|
version: esp32s3
|
||||||
epd_variant: [213epd, 29epd]
|
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:
|
steps:
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
@ -45,16 +55,51 @@ jobs:
|
||||||
path: .pio
|
path: .pio
|
||||||
- name: Install esptools.py
|
- name: Install esptools.py
|
||||||
run: pip install --upgrade esptool
|
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
|
- name: Create merged firmware binary
|
||||||
run: mkdir -p ${{ matrix.chip.name }}_${{ matrix.epd_variant }} && esptool.py --chip ${{ matrix.chip.version }} merge_bin -o ${{ matrix.chip.name }}_${{ matrix.epd_variant }}/${{ matrix.chip.name }}_${{ matrix.epd_variant }}.bin --flash_mode dio 0x0000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/bootloader.bin 0x8000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/partitions.bin 0xe000 .pio/boot_app0.bin 0x10000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/firmware.bin 0x369000 .pio/build/${{ matrix.chip.name }}_${{ matrix.epd_variant }}/littlefs.bin
|
run: |
|
||||||
|
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 checksum for merged binary
|
- name: Create checksum for merged binary
|
||||||
run: shasum -a 256 ${{ matrix.chip.name }}_${{ matrix.epd_variant }}/${{ matrix.chip.name }}_${{ matrix.epd_variant }}.bin | awk '{print $1}' > ${{ matrix.chip.name }}_${{ matrix.epd_variant }}/${{ matrix.chip.name }}_${{ matrix.epd_variant }}.sha256
|
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
|
||||||
|
|
||||||
- name: Copy all artifacts to output folder
|
- 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 }}
|
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
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
@ -102,8 +147,8 @@ jobs:
|
||||||
with:
|
with:
|
||||||
source-directory: .
|
source-directory: .
|
||||||
target-directory: firmware_v3/
|
target-directory: firmware_v3/
|
||||||
destination-github-username: 'btclock'
|
destination-github-username: "btclock"
|
||||||
destination-repository-name: 'web-flasher'
|
destination-repository-name: "web-flasher"
|
||||||
target-branch: btclock
|
target-branch: main
|
||||||
user-name: ${{github.actor}}
|
user-name: ${{github.actor}}
|
||||||
user-email: ${{github.actor}}@users.noreply.github.com
|
user-email: ${{github.actor}}@users.noreply.github.com
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -11,3 +11,4 @@ data/node_modules
|
||||||
node_modules
|
node_modules
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.bin
|
*.bin
|
||||||
|
ci/cache
|
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -1,3 +1,3 @@
|
||||||
[submodule "data"]
|
[submodule "data"]
|
||||||
path = data
|
path = data
|
||||||
url = https://github.com/btclock/webui.git
|
url = https://git.btclock.dev/btclock/webui.git
|
||||||
|
|
44
README.md
44
README.md
|
@ -1,6 +1,8 @@
|
||||||
# BTClock v3
|
# BTClock v3
|
||||||
|
|
||||||
[![BTClock CI](https://github.com/btclock/btclock_v3/actions/workflows/tagging.yml/badge.svg)](https://github.com/btclock/btclock_v3/actions/workflows/tagging.yml)
|
[![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)
|
||||||
|
|
||||||
Software for the BTClock project.
|
Software for the BTClock project.
|
||||||
|
|
||||||
|
@ -12,12 +14,46 @@ Biggest differences with v2 are:
|
||||||
- Added market capitalization screen
|
- Added market capitalization screen
|
||||||
- LED flash on new block (and focus to block height screen on new block)
|
- 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.
|
"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.
|
||||||
|
|
||||||
Most [information](https://github.com/btclock/btclock_v2/wiki) about BTClock v2 is still valid for this version.
|
See the [docs](https://git.btclock.dev/btclock/docs) repo for more information and building instructions.
|
||||||
|
|
||||||
**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.
|
**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.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
Use PlatformIO to build it yourself. Make sure you fetch the [WebUI](https://github.com/btclock/webui) submodule.
|
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.
|
||||||
|
|
60
boards/btclock_rev_b.json
Normal file
60
boards/btclock_rev_b.json
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino":{
|
||||||
|
"ldscript": "esp32s3_out.ld",
|
||||||
|
"partitions": "default_8MB.csv"
|
||||||
|
},
|
||||||
|
"core": "esp32",
|
||||||
|
"extra_flags": [
|
||||||
|
"-DBOARD_HAS_PSRAM",
|
||||||
|
"-DARDUINO_BTCLOCK_REV_B",
|
||||||
|
"-DARDUINO_ESP32S3_DEV",
|
||||||
|
"-DIS_BTCLOCK_REV_B",
|
||||||
|
"-DARDUINO_USB_MODE=1",
|
||||||
|
"-DARDUINO_RUNNING_CORE=1",
|
||||||
|
"-DARDUINO_EVENT_RUNNING_CORE=1",
|
||||||
|
"-DARDUINO_USB_CDC_ON_BOOT=1"
|
||||||
|
],
|
||||||
|
"f_cpu": "240000000L",
|
||||||
|
"f_flash": "80000000L",
|
||||||
|
"flash_mode": "qio",
|
||||||
|
"espidf": {
|
||||||
|
"sdkconfig_path": "boards"
|
||||||
|
},
|
||||||
|
"hwids": [
|
||||||
|
[
|
||||||
|
"0x303A",
|
||||||
|
"0x1001"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"mcu": "esp32s3",
|
||||||
|
"variant": "esp32s3"
|
||||||
|
},
|
||||||
|
"connectivity": [
|
||||||
|
"bluetooth",
|
||||||
|
"wifi"
|
||||||
|
],
|
||||||
|
"debug": {
|
||||||
|
"default_tool": "esp-builtin",
|
||||||
|
"onboard_tools": [
|
||||||
|
"esp-builtin"
|
||||||
|
],
|
||||||
|
"openocd_target": "esp32s3.cfg"
|
||||||
|
},
|
||||||
|
"frameworks": [
|
||||||
|
"arduino",
|
||||||
|
"espidf"
|
||||||
|
],
|
||||||
|
"name": "BTClock (rev. B)",
|
||||||
|
"upload": {
|
||||||
|
"flash_size": "8MB",
|
||||||
|
"maximum_ram_size": 327680,
|
||||||
|
"maximum_size": 8388608,
|
||||||
|
"use_1200bps_touch": true,
|
||||||
|
"wait_for_upload_port": true,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"speed": 460800
|
||||||
|
},
|
||||||
|
"url": "http://github.com/btclock",
|
||||||
|
"vendor": "BTClock"
|
||||||
|
}
|
|
@ -10,7 +10,7 @@
|
||||||
"-DBOARD_HAS_PSRAM",
|
"-DBOARD_HAS_PSRAM",
|
||||||
"-DARDUINO_BTCLOCK",
|
"-DARDUINO_BTCLOCK",
|
||||||
"-DARDUINO_ESP32S3_DEV",
|
"-DARDUINO_ESP32S3_DEV",
|
||||||
"-DIS_BTCLOCK_S3",
|
"-DIS_BTCLOCK_V8",
|
||||||
"-DARDUINO_USB_MODE=1",
|
"-DARDUINO_USB_MODE=1",
|
||||||
"-DARDUINO_RUNNING_CORE=1",
|
"-DARDUINO_RUNNING_CORE=1",
|
||||||
"-DARDUINO_EVENT_RUNNING_CORE=1",
|
"-DARDUINO_EVENT_RUNNING_CORE=1",
|
||||||
|
@ -20,8 +20,8 @@
|
||||||
"f_flash": "80000000L",
|
"f_flash": "80000000L",
|
||||||
"flash_mode": "qio",
|
"flash_mode": "qio",
|
||||||
"psram_type": "opi",
|
"psram_type": "opi",
|
||||||
"espidf": {
|
"esp-idf": {
|
||||||
"sdkconfig_path": "boards"
|
"sdkconfig_path": "boards/sdkconfig.btclock_v8"
|
||||||
},
|
},
|
||||||
"hwids": [
|
"hwids": [
|
||||||
[
|
[
|
1598
boards/sdkconfig.btclock_v8
Normal file
1598
boards/sdkconfig.btclock_v8
Normal file
File diff suppressed because it is too large
Load diff
15
ci/Dockerfile
Normal file
15
ci/Dockerfile
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# 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 dcdf98964a42ccd83b2d2501bbed46a640bbc300
|
Subproject commit 924be8fc2eb02fe384a20b53da1a9fa3d8db8a05
|
|
@ -3,7 +3,7 @@ dependencies:
|
||||||
component_hash: null
|
component_hash: null
|
||||||
source:
|
source:
|
||||||
type: idf
|
type: idf
|
||||||
version: 4.4.6
|
version: 4.4.7
|
||||||
manifest_hash: f4c10dfb616cf7e24f85cb263b8c89ef7d6d8eee64860fd27097b1a83ba56960
|
manifest_hash: cd2f3ee15e776d949eb4ea4eddc8f39b30c2a7905050850eed01ab4928143cff
|
||||||
target: esp32s3
|
target: esp32s3
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
|
52
lib/btclock/bitaxe_handler.cpp
Normal file
52
lib/btclock/bitaxe_handler.cpp
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#include "bitaxe_handler.hpp"
|
||||||
|
|
||||||
|
std::array<std::string, NUM_SCREENS> parseBitaxeHashRate(std::string text)
|
||||||
|
{
|
||||||
|
std::array<std::string, NUM_SCREENS> ret;
|
||||||
|
ret.fill(""); // Initialize all elements to empty strings
|
||||||
|
|
||||||
|
std::size_t textLength = text.length();
|
||||||
|
|
||||||
|
// Calculate the position where the digits should start
|
||||||
|
// Account for the position of the "mdi:pickaxe" and the "GH/S" label
|
||||||
|
std::size_t startIndex = NUM_SCREENS - 1 - textLength;
|
||||||
|
|
||||||
|
// Insert the "mdi: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] = text.substr(i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret[NUM_SCREENS - 1] = "GH/S";
|
||||||
|
ret[0] = "mdi:bitaxe";
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<std::string, NUM_SCREENS> parseBitaxeBestDiff(std::string text)
|
||||||
|
{
|
||||||
|
std::array<std::string, NUM_SCREENS> ret;
|
||||||
|
std::uint32_t firstIndex = 0;
|
||||||
|
|
||||||
|
if (text.length() < NUM_SCREENS)
|
||||||
|
{
|
||||||
|
text.insert(text.begin(), NUM_SCREENS - text.length(), ' ');
|
||||||
|
ret[0] = "mdi:bitaxe";
|
||||||
|
ret[1] = "mdi:rocket";
|
||||||
|
firstIndex = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::uint8_t i = firstIndex; i < NUM_SCREENS; i++)
|
||||||
|
{
|
||||||
|
ret[i] = text[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
5
lib/btclock/bitaxe_handler.hpp
Normal file
5
lib/btclock/bitaxe_handler.hpp
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
std::array<std::string, NUM_SCREENS> parseBitaxeHashRate(std::string text);
|
||||||
|
std::array<std::string, NUM_SCREENS> parseBitaxeBestDiff(std::string text);
|
|
@ -1,33 +1,138 @@
|
||||||
#include "data_handler.hpp"
|
#include "data_handler.hpp"
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
#include <emscripten.h>
|
||||||
|
#include <emscripten/bind.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
std::array<std::string, NUM_SCREENS> parsePriceData(std::uint32_t price, char currencySymbol, bool useSuffixFormat)
|
char getCurrencySymbol(char input)
|
||||||
|
{
|
||||||
|
switch (input)
|
||||||
|
{
|
||||||
|
case CURRENCY_EUR:
|
||||||
|
return '[';
|
||||||
|
break;
|
||||||
|
case CURRENCY_GBP:
|
||||||
|
return ']';
|
||||||
|
break;
|
||||||
|
case CURRENCY_JPY:
|
||||||
|
return '^';
|
||||||
|
break;
|
||||||
|
case CURRENCY_AUD:
|
||||||
|
case CURRENCY_CAD:
|
||||||
|
case CURRENCY_USD:
|
||||||
|
return '$';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getCurrencyCode(char input)
|
||||||
|
{
|
||||||
|
switch (input)
|
||||||
|
{
|
||||||
|
case CURRENCY_EUR:
|
||||||
|
return CURRENCY_CODE_EUR;
|
||||||
|
break;
|
||||||
|
case CURRENCY_GBP:
|
||||||
|
return CURRENCY_CODE_GBP;
|
||||||
|
break;
|
||||||
|
case CURRENCY_JPY:
|
||||||
|
return CURRENCY_CODE_JPY;
|
||||||
|
break;
|
||||||
|
case CURRENCY_AUD:
|
||||||
|
return CURRENCY_CODE_AUD;
|
||||||
|
break;
|
||||||
|
case CURRENCY_CAD:
|
||||||
|
return CURRENCY_CODE_CAD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return CURRENCY_CODE_USD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char getCurrencyChar(const std::string& input)
|
||||||
|
{
|
||||||
|
if (input == "EUR")
|
||||||
|
return CURRENCY_EUR;
|
||||||
|
else if (input == "GBP")
|
||||||
|
return CURRENCY_GBP;
|
||||||
|
else if (input == "JPY")
|
||||||
|
return CURRENCY_JPY;
|
||||||
|
else if (input == "AUD")
|
||||||
|
return CURRENCY_AUD;
|
||||||
|
else if (input == "CAD")
|
||||||
|
return CURRENCY_CAD;
|
||||||
|
else
|
||||||
|
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> ret;
|
std::array<std::string, NUM_SCREENS> ret;
|
||||||
std::string priceString;
|
std::string priceString;
|
||||||
if (std::to_string(price).length() >= NUM_SCREENS || useSuffixFormat) {
|
if (std::to_string(price).length() >= NUM_SCREENS || useSuffixFormat)
|
||||||
priceString = currencySymbol + formatNumberWithSuffix(price, NUM_SCREENS-2);
|
|
||||||
} else {
|
|
||||||
priceString = currencySymbol + std::to_string(price);
|
|
||||||
}
|
|
||||||
std::uint32_t firstIndex = 0;
|
|
||||||
if (priceString.length() < (NUM_SCREENS))
|
|
||||||
{
|
{
|
||||||
priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' ');
|
int numScreens = shareDot || mowMode ? NUM_SCREENS - 1 : NUM_SCREENS - 2;
|
||||||
if (currencySymbol == '[')
|
priceString = getCurrencySymbol(currencySymbol) + formatNumberWithSuffix(price, numScreens, mowMode);
|
||||||
{
|
|
||||||
ret[0] = "BTC/EUR";
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret[0] = "BTC/USD";
|
priceString = getCurrencySymbol(currencySymbol) + std::to_string(price);
|
||||||
}
|
}
|
||||||
|
std::uint32_t firstIndex = 0;
|
||||||
|
if ((shareDot && priceString.length() <= (NUM_SCREENS)) || priceString.length() < (NUM_SCREENS))
|
||||||
|
{
|
||||||
|
priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' ');
|
||||||
|
|
||||||
|
if (mowMode)
|
||||||
|
{
|
||||||
|
ret[0] = "MOW/UNITS";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret[0] = "BTC/" + getCurrencyCode(currencySymbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
firstIndex = 1;
|
firstIndex = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t dotPosition = priceString.find('.');
|
||||||
|
|
||||||
|
if (shareDot && dotPosition != std::string::npos && dotPosition > 0)
|
||||||
|
{
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
for (std::uint32_t i = firstIndex; i < NUM_SCREENS; i++)
|
for (std::uint32_t i = firstIndex; i < NUM_SCREENS; i++)
|
||||||
{
|
{
|
||||||
ret[i] = priceString[i];
|
ret[i] = std::string(1, priceString[i]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -37,21 +142,34 @@ std::array<std::string, NUM_SCREENS> parseSatsPerCurrency(std::uint32_t price, c
|
||||||
std::array<std::string, NUM_SCREENS> ret;
|
std::array<std::string, NUM_SCREENS> ret;
|
||||||
std::string priceString = std::to_string(int(round(1 / float(price) * 10e7)));
|
std::string priceString = std::to_string(int(round(1 / float(price) * 10e7)));
|
||||||
std::uint32_t firstIndex = 0;
|
std::uint32_t firstIndex = 0;
|
||||||
uint insertSatSymbol = NUM_SCREENS - priceString.length() - 1;
|
std::uint8_t insertSatSymbol = NUM_SCREENS - priceString.length() - 1;
|
||||||
|
|
||||||
if (priceString.length() < (NUM_SCREENS))
|
if (priceString.length() < (NUM_SCREENS))
|
||||||
{
|
{
|
||||||
priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' ');
|
// Check if price is greater than 1 billion
|
||||||
|
if (price >= 100000000)
|
||||||
|
|
||||||
if (currencySymbol == '[')
|
|
||||||
{
|
{
|
||||||
ret[0] = "SATS/EUR";
|
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
|
else
|
||||||
{
|
{
|
||||||
ret[0] = "MSCW/TIME";
|
priceString = std::to_string(static_cast<int>(round(1.0 / static_cast<double>(price) * 1e8))); // Default formatting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
ret[0] = "SATS/" + getCurrencyCode(currencySymbol);
|
||||||
|
else
|
||||||
|
ret[0] = "MSCW/TIME";
|
||||||
|
|
||||||
firstIndex = 1;
|
firstIndex = 1;
|
||||||
|
|
||||||
for (std::uint32_t i = firstIndex; i < NUM_SCREENS; i++)
|
for (std::uint32_t i = firstIndex; i < NUM_SCREENS; i++)
|
||||||
|
@ -59,7 +177,8 @@ std::array<std::string, NUM_SCREENS> parseSatsPerCurrency(std::uint32_t price, c
|
||||||
ret[i] = priceString[i];
|
ret[i] = priceString[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (withSatsSymbol) {
|
if (withSatsSymbol)
|
||||||
|
{
|
||||||
ret[insertSatSymbol] = "STS";
|
ret[insertSatSymbol] = "STS";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +206,8 @@ std::array<std::string, NUM_SCREENS> parseBlockHeight(std::uint32_t blockHeight)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<std::string, NUM_SCREENS> parseBlockFees(std::uint16_t blockFees) {
|
std::array<std::string, NUM_SCREENS> parseBlockFees(std::uint16_t blockFees)
|
||||||
|
{
|
||||||
std::array<std::string, NUM_SCREENS> ret;
|
std::array<std::string, NUM_SCREENS> ret;
|
||||||
std::string blockFeesString = std::to_string(blockFees);
|
std::string blockFeesString = std::to_string(blockFees);
|
||||||
std::uint32_t firstIndex = 0;
|
std::uint32_t firstIndex = 0;
|
||||||
|
@ -99,7 +219,7 @@ std::array<std::string, NUM_SCREENS> parseBlockFees(std::uint16_t blockFees) {
|
||||||
firstIndex = 1;
|
firstIndex = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint i = firstIndex; i < NUM_SCREENS-1; i++)
|
for (std::uint8_t i = firstIndex; i < NUM_SCREENS - 1; i++)
|
||||||
{
|
{
|
||||||
ret[i] = blockFeesString[i];
|
ret[i] = blockFeesString[i];
|
||||||
}
|
}
|
||||||
|
@ -115,7 +235,8 @@ std::array<std::string, NUM_SCREENS> parseHalvingCountdown(std::uint32_t blockHe
|
||||||
const std::uint32_t nextHalvingBlock = 210000 - (blockHeight % 210000);
|
const std::uint32_t nextHalvingBlock = 210000 - (blockHeight % 210000);
|
||||||
const std::uint32_t minutesToHalving = nextHalvingBlock * 10;
|
const std::uint32_t minutesToHalving = nextHalvingBlock * 10;
|
||||||
|
|
||||||
if (asBlocks) {
|
if (asBlocks)
|
||||||
|
{
|
||||||
std::string blockNrString = std::to_string(nextHalvingBlock);
|
std::string blockNrString = std::to_string(nextHalvingBlock);
|
||||||
std::uint32_t firstIndex = 0;
|
std::uint32_t firstIndex = 0;
|
||||||
|
|
||||||
|
@ -130,9 +251,9 @@ std::array<std::string, NUM_SCREENS> parseHalvingCountdown(std::uint32_t blockHe
|
||||||
{
|
{
|
||||||
ret[i] = blockNrString[i];
|
ret[i] = blockNrString[i];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
else
|
||||||
|
{
|
||||||
|
|
||||||
const int years = floor(minutesToHalving / 525600);
|
const int years = floor(minutesToHalving / 525600);
|
||||||
const int days = floor((minutesToHalving - (years * 525600)) / (24 * 60));
|
const int days = floor((minutesToHalving - (years * 525600)) / (24 * 60));
|
||||||
|
@ -155,15 +276,9 @@ std::array<std::string, NUM_SCREENS> parseMarketCap(std::uint32_t blockHeight, s
|
||||||
std::array<std::string, NUM_SCREENS> ret;
|
std::array<std::string, NUM_SCREENS> ret;
|
||||||
std::uint32_t firstIndex = 0;
|
std::uint32_t firstIndex = 0;
|
||||||
double supply = getSupplyAtBlock(blockHeight);
|
double supply = getSupplyAtBlock(blockHeight);
|
||||||
int64_t marketCap = static_cast<std::int64_t>(supply * double(price));
|
uint64_t marketCap = static_cast<std::uint64_t>(supply * double(price));
|
||||||
if (currencySymbol == '[')
|
|
||||||
{
|
ret[0] = getCurrencyCode(currencySymbol) + "/MCAP";
|
||||||
ret[0] = "EUR/MCAP";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ret[0] = "USD/MCAP";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bigChars)
|
if (bigChars)
|
||||||
{
|
{
|
||||||
|
@ -197,7 +312,7 @@ std::array<std::string, NUM_SCREENS> parseMarketCap(std::uint32_t blockHeight, s
|
||||||
ret[i] = "";
|
ret[i] = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
ret[NUM_SCREENS - groups - 1] = " $ ";
|
ret[NUM_SCREENS - groups - 1] = std::string(" ") + currencySymbol + " ";
|
||||||
for (std::uint32_t i = 0; i < groups; i++)
|
for (std::uint32_t i = 0; i < groups; i++)
|
||||||
{
|
{
|
||||||
ret[(NUM_SCREENS - groups + i)] = stringValue.substr(i * 3, 3).c_str();
|
ret[(NUM_SCREENS - groups + i)] = stringValue.substr(i * 3, 3).c_str();
|
||||||
|
@ -206,3 +321,70 @@ std::array<std::string, NUM_SCREENS> parseMarketCap(std::uint32_t blockHeight, s
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
emscripten::val arrayToStringArray(const std::array<std::string, NUM_SCREENS> &arr)
|
||||||
|
{
|
||||||
|
emscripten::val jsArray = emscripten::val::array();
|
||||||
|
for (const auto &str : arr)
|
||||||
|
{
|
||||||
|
jsArray.call<void>("push", str);
|
||||||
|
}
|
||||||
|
return jsArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
emscripten::val vectorToStringArray(const std::vector<std::string> &vec)
|
||||||
|
{
|
||||||
|
emscripten::val jsArray = emscripten::val::array();
|
||||||
|
for (size_t i = 0; i < vec.size(); ++i)
|
||||||
|
{
|
||||||
|
jsArray.set(i, vec[i]);
|
||||||
|
}
|
||||||
|
return jsArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
return arrayToStringArray(parsePriceData(price, currencySymbol[0], useSuffixFormat, mowMode, shareDot));
|
||||||
|
}
|
||||||
|
|
||||||
|
emscripten::val parseHalvingCountdownArray(std::uint32_t blockHeight, bool asBlocks)
|
||||||
|
{
|
||||||
|
return arrayToStringArray(parseHalvingCountdown(blockHeight, asBlocks));
|
||||||
|
}
|
||||||
|
|
||||||
|
emscripten::val parseMarketCapArray(std::uint32_t blockHeight, std::uint32_t price, const std::string ¤cySymbol, bool bigChars)
|
||||||
|
{
|
||||||
|
return arrayToStringArray(parseMarketCap(blockHeight, price, currencySymbol[0], bigChars));
|
||||||
|
}
|
||||||
|
|
||||||
|
emscripten::val parseBlockFeesArray(std::uint16_t blockFees)
|
||||||
|
{
|
||||||
|
return arrayToStringArray(parseBlockFees(blockFees));
|
||||||
|
}
|
||||||
|
|
||||||
|
emscripten::val parseSatsPerCurrencyArray(std::uint32_t price, const std::string ¤cySymbol, bool withSatsSymbol)
|
||||||
|
{
|
||||||
|
return arrayToStringArray(parseSatsPerCurrency(price, currencySymbol[0], withSatsSymbol));
|
||||||
|
}
|
||||||
|
|
||||||
|
EMSCRIPTEN_BINDINGS(my_module)
|
||||||
|
{
|
||||||
|
// emscripten::register_vector<std::string>("StringList");
|
||||||
|
|
||||||
|
emscripten::function("parseBlockHeight", &parseBlockHeightArray);
|
||||||
|
emscripten::function("parseHalvingCountdown", &parseHalvingCountdownArray);
|
||||||
|
emscripten::function("parseMarketCap", &parseMarketCapArray);
|
||||||
|
emscripten::function("parseBlockFees", &parseBlockFeesArray);
|
||||||
|
emscripten::function("parseSatsPerCurrency", &parseSatsPerCurrencyArray);
|
||||||
|
emscripten::function("parsePriceData", &parsePriceDataArray);
|
||||||
|
|
||||||
|
emscripten::function("arrayToStringArray", &arrayToStringArray);
|
||||||
|
emscripten::function("vectorToStringArray", &vectorToStringArray);
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -2,12 +2,31 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "utils.hpp"
|
#include "utils.hpp"
|
||||||
|
|
||||||
std::array<std::string, NUM_SCREENS> parsePriceData(std::uint32_t price, char currencySymbol, bool useSuffixFormat = false);
|
const char CURRENCY_USD = '$';
|
||||||
|
const char CURRENCY_EUR = '[';
|
||||||
|
const char CURRENCY_GBP = ']';
|
||||||
|
const char CURRENCY_JPY = '^';
|
||||||
|
const char CURRENCY_AUD = '_';
|
||||||
|
const char CURRENCY_CAD = '`';
|
||||||
|
|
||||||
|
const std::string CURRENCY_CODE_USD = "USD";
|
||||||
|
const std::string CURRENCY_CODE_EUR = "EUR";
|
||||||
|
const std::string CURRENCY_CODE_GBP = "GBP";
|
||||||
|
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> parseSatsPerCurrency(std::uint32_t price, char currencySymbol, bool withSatsSymbol);
|
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> parseBlockHeight(std::uint32_t blockHeight);
|
||||||
std::array<std::string, NUM_SCREENS> parseHalvingCountdown(std::uint32_t blockHeight, bool asBlocks);
|
std::array<std::string, NUM_SCREENS> parseHalvingCountdown(std::uint32_t blockHeight, bool asBlocks);
|
||||||
std::array<std::string, NUM_SCREENS> parseMarketCap(std::uint32_t blockHeight, std::uint32_t price, char currencySymbol, bool bigChars);
|
std::array<std::string, NUM_SCREENS> parseMarketCap(std::uint32_t blockHeight, std::uint32_t price, char currencySymbol, bool bigChars);
|
||||||
std::array<std::string, NUM_SCREENS> parseBlockFees(std::uint16_t blockFees);
|
std::array<std::string, NUM_SCREENS> parseBlockFees(std::uint16_t blockFees);
|
||||||
|
|
||||||
|
char getCurrencySymbol(char input);
|
||||||
|
std::string getCurrencyCode(char input);
|
||||||
|
char getCurrencyChar(const std::string& input);
|
24
lib/btclock/nostrdisplay_handler.cpp
Normal file
24
lib/btclock/nostrdisplay_handler.cpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#include "nostrdisplay_handler.hpp"
|
||||||
|
|
||||||
|
std::array<std::string, NUM_SCREENS> parseZapNotify(std::uint16_t amount, bool withSatsSymbol)
|
||||||
|
{
|
||||||
|
std::string text = std::to_string(amount);
|
||||||
|
std::size_t textLength = text.length();
|
||||||
|
std::size_t startIndex = NUM_SCREENS - textLength;
|
||||||
|
|
||||||
|
std::array<std::string, NUM_SCREENS> textEpdContent = {"ZAP", "mdi-lnbolt", "", "", "", "", ""};
|
||||||
|
|
||||||
|
// Insert the sats symbol just before the digits
|
||||||
|
if (startIndex > 0 && withSatsSymbol)
|
||||||
|
{
|
||||||
|
textEpdContent[startIndex - 1] = "STS";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Place the digits
|
||||||
|
for (std::size_t i = 0; i < textLength; i++)
|
||||||
|
{
|
||||||
|
textEpdContent[startIndex + i] = text.substr(i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return textEpdContent;
|
||||||
|
}
|
5
lib/btclock/nostrdisplay_handler.hpp
Normal file
5
lib/btclock/nostrdisplay_handler.hpp
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
std::array<std::string, NUM_SCREENS> parseZapNotify(std::uint16_t amount, bool withSatsSymbol);
|
|
@ -29,6 +29,11 @@ double getSupplyAtBlock(std::uint32_t blockNr)
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters)
|
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)
|
||||||
{
|
{
|
||||||
static char result[20]; // Adjust size as needed
|
static char result[20]; // Adjust size as needed
|
||||||
const long long quadrillion = 1000000000000000LL;
|
const long long quadrillion = 1000000000000000LL;
|
||||||
|
@ -56,30 +61,185 @@ std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters)
|
||||||
numDouble /= billion;
|
numDouble /= billion;
|
||||||
suffix = 'B';
|
suffix = 'B';
|
||||||
}
|
}
|
||||||
else if (num >= million || numDigits > 6)
|
else if (num >= million || numDigits > 6 || (mowMode && num >= thousand))
|
||||||
{
|
{
|
||||||
numDouble /= million;
|
numDouble /= million;
|
||||||
suffix = 'M';
|
suffix = 'M';
|
||||||
}
|
}
|
||||||
else if (num >= thousand || numDigits > 3)
|
else if (!mowMode && (num >= thousand || numDigits > 3))
|
||||||
{
|
{
|
||||||
numDouble /= thousand;
|
numDouble /= thousand;
|
||||||
suffix = 'K';
|
suffix = 'K';
|
||||||
}
|
}
|
||||||
else
|
else if (!mowMode)
|
||||||
{
|
{
|
||||||
sprintf(result, "%llu", (unsigned long long)num);
|
snprintf(result, sizeof(result), "%llu", (unsigned long long)num);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
else // mowMode is true and num < 1000
|
||||||
|
{
|
||||||
|
numDouble /= million;
|
||||||
|
suffix = 'M';
|
||||||
|
}
|
||||||
|
|
||||||
// Add suffix
|
// Add suffix
|
||||||
int len = snprintf(result, sizeof(result), "%.0f%c", numDouble, suffix);
|
int len;
|
||||||
|
|
||||||
// If there's room, add decimal places
|
// 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 (len < numCharacters)
|
if (len < numCharacters)
|
||||||
{
|
{
|
||||||
snprintf(result, sizeof(result), "%.*f%c", numCharacters - len - 1, numDouble, suffix);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get sat amount from a bolt11 invoice
|
||||||
|
*
|
||||||
|
* Based on https://github.com/lnbits/nostr-zap-lamp/blob/main/nostrZapLamp/nostrZapLamp.ino
|
||||||
|
*/
|
||||||
|
int64_t getAmountInSatoshis(std::string bolt11) {
|
||||||
|
int64_t number = -1;
|
||||||
|
char multiplier = ' ';
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < bolt11.length(); ++i) {
|
||||||
|
if (isdigit(bolt11[i])) {
|
||||||
|
number = 0;
|
||||||
|
while (isdigit(bolt11[i])) {
|
||||||
|
number = number * 10 + (bolt11[i] - '0');
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
for (unsigned int j = i; j < bolt11.length(); ++j) {
|
||||||
|
if (isalpha(bolt11[j])) {
|
||||||
|
multiplier = bolt11[j];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (number == -1 || multiplier == ' ') {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t satoshis = number;
|
||||||
|
|
||||||
|
switch (multiplier) {
|
||||||
|
case 'm':
|
||||||
|
satoshis *= 100000; // 0.001 * 100,000,000
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
satoshis *= 100; // 0.000001 * 100,000,000
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
satoshis /= 10; // 0.000000001 * 100,000,000
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
satoshis /= 10000; // 0.000000000001 * 100,000,000
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,9 +5,15 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
|
||||||
int modulo(int x,int N);
|
int modulo(int x,int N);
|
||||||
|
|
||||||
double getSupplyAtBlock(std::uint32_t blockNr);
|
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 = 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);
|
20
maintainers.yaml
Normal file
20
maintainers.yaml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
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
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
nvs, data, nvs, 36K, 20K,
|
nvs, data, nvs, 0x9000, 0x5000,
|
||||||
otadata, data, ota, 56K, 8K,
|
otadata, data, ota, 0xe000, 0x2000,
|
||||||
app0, app, ota_0, 64K, 1700K,
|
app0, app, ota_0, 0x10000, 0x1b8000,
|
||||||
app1, app, ota_1, , 1700K,
|
app1, app, ota_1, , 0x1b8000,
|
||||||
spiffs, data, spiffs, , 400K,
|
spiffs, data, spiffs, , 0x66C00,
|
||||||
coredump, data, coredump,, 64K,
|
coredump, data, coredump,, 0x10000,
|
||||||
|
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Name, Type, SubType, Offset, Size, Flags
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
nvs, data, nvs, 36K, 20K,
|
nvs, data, nvs, 0x9000, 0x5000,
|
||||||
otadata, data, ota, 56K, 8K,
|
otadata, data, ota, 0xe000, 0x2000,
|
||||||
app0, app, ota_0, 64K, 4096K,
|
app0, app, ota_0, 0x10000, 0x6F0000,
|
||||||
app1, app, ota_1, , 4096K,
|
app1, app, ota_1, , 0x6F0000,
|
||||||
spiffs, data, spiffs, , 3072K,
|
spiffs, data, spiffs, , 0x200000,
|
||||||
coredump, data, coredump,, 64K,
|
coredump, data, coredump,, 0x10000,
|
|
7
partition_8mb.csv
Normal file
7
partition_8mb.csv
Normal file
|
@ -0,0 +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,
|
|
115
platformio.ini
115
platformio.ini
|
@ -7,20 +7,24 @@
|
||||||
;
|
;
|
||||||
; Please visit documentation for the other options and examples
|
; Please visit documentation for the other options and examples
|
||||||
; https://docs.platformio.org/page/projectconf.html
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
[platformio]
|
[platformio]
|
||||||
data_dir = data/build_gz
|
data_dir = data/build_gz
|
||||||
default_envs = lolin_s3_mini_213epd, lolin_s3_mini_29epd
|
default_envs = lolin_s3_mini_213epd, lolin_s3_mini_29epd, btclock_rev_b_213epd, btclock_v8_213epd
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
|
|
||||||
|
|
||||||
[btclock_base]
|
[btclock_base]
|
||||||
platform = espressif32 @ ^6.5.0
|
platform = espressif32 @ ^6.9.0
|
||||||
framework = arduino, espidf
|
framework = arduino, espidf
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_filters = esp32_exception_decoder, colorize
|
monitor_filters = esp32_exception_decoder, colorize
|
||||||
board_build.filesystem = littlefs
|
board_build.filesystem = littlefs
|
||||||
extra_scripts = post:scripts/extra_script.py
|
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 =
|
build_flags =
|
||||||
!python scripts/git_rev.py
|
!python scripts/git_rev.py
|
||||||
-DLAST_BUILD_TIME=$UNIX_TIME
|
-DLAST_BUILD_TIME=$UNIX_TIME
|
||||||
|
@ -32,15 +36,14 @@ build_unflags =
|
||||||
-fno-exceptions
|
-fno-exceptions
|
||||||
lib_deps =
|
lib_deps =
|
||||||
https://github.com/joltwallet/esp_littlefs.git
|
https://github.com/joltwallet/esp_littlefs.git
|
||||||
bblanchon/ArduinoJson@^7.0.3
|
bblanchon/ArduinoJson@^7.2.1
|
||||||
esphome/Improv@^1.2.3
|
mathieucarbou/ESPAsyncWebServer @ 3.3.23
|
||||||
mathieucarbou/ESP Async WebServer
|
robtillaart/MCP23017@^0.8.0
|
||||||
adafruit/Adafruit BusIO@^1.15.0
|
adafruit/Adafruit NeoPixel@^1.12.3
|
||||||
adafruit/Adafruit MCP23017 Arduino Library@^2.3.2
|
https://github.com/dsbaars/universal_pin#feature/mcp23017_rt
|
||||||
adafruit/Adafruit NeoPixel@^1.12.0
|
|
||||||
https://github.com/dsbaars/universal_pin
|
|
||||||
https://github.com/dsbaars/GxEPD2#universal_pin
|
https://github.com/dsbaars/GxEPD2#universal_pin
|
||||||
https://github.com/tzapu/WiFiManager.git#v2.0.16-rc.2
|
https://github.com/tzapu/WiFiManager.git#v2.0.17
|
||||||
|
rblb/Nostrduino@1.2.8
|
||||||
|
|
||||||
[env:lolin_s3_mini]
|
[env:lolin_s3_mini]
|
||||||
extends = btclock_base
|
extends = btclock_base
|
||||||
|
@ -54,8 +57,39 @@ build_flags =
|
||||||
-D NUM_SCREENS=7
|
-D NUM_SCREENS=7
|
||||||
-D I2C_SDA_PIN=35
|
-D I2C_SDA_PIN=35
|
||||||
-D I2C_SCK_PIN=36
|
-D I2C_SCK_PIN=36
|
||||||
|
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||||
|
-D IS_HW_REV_A
|
||||||
build_unflags =
|
build_unflags =
|
||||||
${btclock_base.build_unflags}
|
${btclock_base.build_unflags}
|
||||||
|
platform_packages =
|
||||||
|
platformio/tool-mklittlefs@^1.203.210628
|
||||||
|
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||||
|
|
||||||
|
[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
|
||||||
|
lib_deps =
|
||||||
|
${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
|
||||||
|
|
||||||
[env:lolin_s3_mini_213epd]
|
[env:lolin_s3_mini_213epd]
|
||||||
extends = env:lolin_s3_mini
|
extends = env:lolin_s3_mini
|
||||||
|
@ -64,6 +98,22 @@ build_flags =
|
||||||
${env:lolin_s3_mini.build_flags}
|
${env:lolin_s3_mini.build_flags}
|
||||||
-D USE_QR
|
-D USE_QR
|
||||||
-D VERSION_EPD_2_13
|
-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
|
||||||
|
|
||||||
|
[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
|
||||||
|
|
||||||
[env:lolin_s3_mini_29epd]
|
[env:lolin_s3_mini_29epd]
|
||||||
extends = env:lolin_s3_mini
|
extends = env:lolin_s3_mini
|
||||||
|
@ -72,11 +122,28 @@ build_flags =
|
||||||
${env:lolin_s3_mini.build_flags}
|
${env:lolin_s3_mini.build_flags}
|
||||||
-D USE_QR
|
-D USE_QR
|
||||||
-D VERSION_EPD_2_9
|
-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
|
||||||
|
|
||||||
[env:btclock_s3]
|
[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
|
||||||
|
|
||||||
|
[env:btclock_v8]
|
||||||
extends = btclock_base
|
extends = btclock_base
|
||||||
board = btclock
|
board = btclock_v8
|
||||||
board_build.partitions = partition_16mb.csv
|
board_build.partitions = partition_16mb.csv
|
||||||
|
board_build.flash_mode = qio
|
||||||
test_framework = unity
|
test_framework = unity
|
||||||
build_flags =
|
build_flags =
|
||||||
${btclock_base.build_flags}
|
${btclock_base.build_flags}
|
||||||
|
@ -97,6 +164,21 @@ build_flags =
|
||||||
-D MCP2_A2_PIN=14
|
-D MCP2_A2_PIN=14
|
||||||
build_unflags =
|
build_unflags =
|
||||||
${btclock_base.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
|
||||||
|
|
||||||
[env:native_test_only]
|
[env:native_test_only]
|
||||||
platform = native
|
platform = native
|
||||||
|
@ -107,3 +189,8 @@ build_flags =
|
||||||
-D NEOPIXEL_PIN=34
|
-D NEOPIXEL_PIN=34
|
||||||
-D NEOPIXEL_COUNT=4
|
-D NEOPIXEL_COUNT=4
|
||||||
-D NUM_SCREENS=7
|
-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
|
||||||
|
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
platformio
|
|
@ -1,8 +1,18 @@
|
||||||
Import("env")
|
Import("env")
|
||||||
import os
|
import os
|
||||||
import gzip
|
import gzip
|
||||||
from shutil import copyfileobj, rmtree
|
from shutil import copyfileobj, rmtree, copyfile, copytree
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
revision = (
|
||||||
|
subprocess.check_output(["git", "rev-parse", "HEAD"])
|
||||||
|
.strip()
|
||||||
|
.decode("utf-8")
|
||||||
|
)
|
||||||
|
|
||||||
def gzip_file(input_file, output_file):
|
def gzip_file(input_file, output_file):
|
||||||
with open(input_file, 'rb') as f_in:
|
with open(input_file, 'rb') as f_in:
|
||||||
|
@ -19,20 +29,97 @@ def process_directory(input_dir, output_dir):
|
||||||
Path(output_root).mkdir(parents=True, exist_ok=True)
|
Path(output_root).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
# if file.endswith(('.html', '.css', '.js')):
|
# if not file.endswith(('.bin')):
|
||||||
input_file_path = os.path.join(root, file)
|
input_file_path = os.path.join(root, file)
|
||||||
output_file_path = os.path.join(output_root, file + '.gz')
|
output_file_path = os.path.join(output_root, file + '.gz')
|
||||||
gzip_file(input_file_path, output_file_path)
|
gzip_file(input_file_path, output_file_path)
|
||||||
print(f'Compressed: {input_file_path} -> {output_file_path}')
|
print(f'Compressed: {input_file_path} -> {output_file_path}')
|
||||||
|
file_path = os.path.join(output_dir, "fs_hash.txt")
|
||||||
|
with open(file_path, "w") as file:
|
||||||
|
file.write(revision)
|
||||||
|
|
||||||
|
|
||||||
# Build web interface before building FS
|
# Build web interface before building FS
|
||||||
def before_buildfs(source, target, env):
|
def before_buildfs(source, target, env):
|
||||||
|
|
||||||
env.Execute("cd data && yarn && yarn postinstall && yarn build")
|
env.Execute("cd data && yarn && yarn postinstall && yarn build")
|
||||||
input_directory = 'data/dist'
|
input_directory = 'data/dist'
|
||||||
output_directory = 'data/build_gz'
|
output_directory = 'data/build_gz'
|
||||||
|
# copytree("assets", "data/dist/assets")
|
||||||
|
|
||||||
process_directory(input_directory, output_directory)
|
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"] = ""
|
os.environ["PUBLIC_BASE_URL"] = ""
|
||||||
env.AddPreAction("$BUILD_DIR/littlefs.bin", before_buildfs)
|
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
|
|
@ -48,8 +48,8 @@ class Listener(ServiceListener):
|
||||||
#arguments = [f"-i {str()} -f -r"]
|
#arguments = [f"-i {str()} -f -r"]
|
||||||
namespace = argparse.Namespace(
|
namespace = argparse.Namespace(
|
||||||
esp_ip=info.parsed_addresses()[0],
|
esp_ip=info.parsed_addresses()[0],
|
||||||
image=f"{os.getcwd()}/.pio/build/lolin_s3_mini_qr/firmware.bin",
|
image=f"{os.getcwd()}/.pio/build/lolin_s3_mini_213epd/firmware.bin",
|
||||||
littlefs=f"{os.getcwd()}/.pio/build/lolin_s3_mini_qr/littlefs.bin",
|
littlefs=f"{os.getcwd()}/.pio/build/lolin_s3_mini_213epd/littlefs.bin",
|
||||||
progress=True
|
progress=True
|
||||||
)
|
)
|
||||||
if (str(info.properties.get(b"version").decode())) != "3.0":
|
if (str(info.properties.get(b"version").decode())) != "3.0":
|
||||||
|
@ -64,7 +64,7 @@ class Listener(ServiceListener):
|
||||||
print("Different version, going to update")
|
print("Different version, going to update")
|
||||||
#espota.serve(namespace.esp_ip, "0.0.0.0", 3232, random.randint(10000,60000), "", namespace.littlefs, SPIFFS)
|
#espota.serve(namespace.esp_ip, "0.0.0.0", 3232, random.randint(10000,60000), "", namespace.littlefs, SPIFFS)
|
||||||
|
|
||||||
#espota.serve(namespace.esp_ip, "0.0.0.0", 3232, random.randint(10000,60000), "", namespace.image, FLASH)
|
espota.serve(namespace.esp_ip, "0.0.0.0", 3232, random.randint(10000,60000), "", namespace.image, FLASH)
|
||||||
#print(arguments)
|
#print(arguments)
|
||||||
|
|
||||||
#logging.basicConfig(level = logging.DEBUG, format = '%(asctime)-8s [%(levelname)s]: %(message)s', datefmt = '%H:%M:%S')
|
#logging.basicConfig(level = logging.DEBUG, format = '%(asctime)-8s [%(levelname)s]: %(message)s', datefmt = '%H:%M:%S')
|
||||||
|
|
|
@ -5,4 +5,17 @@ revision = (
|
||||||
.strip()
|
.strip()
|
||||||
.decode("utf-8")
|
.decode("utf-8")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
tag = (
|
||||||
|
subprocess.check_output(["git", "describe", "--tags", "--exact-match"])
|
||||||
|
.strip()
|
||||||
|
.decode("utf-8")
|
||||||
|
)
|
||||||
|
git_tag_define = '\'-DGIT_TAG=\"%s\"\'' % tag
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
git_tag_define = ''
|
||||||
|
|
||||||
print("'-DGIT_REV=\"%s\"'" % revision)
|
print("'-DGIT_REV=\"%s\"'" % revision)
|
||||||
|
if git_tag_define:
|
||||||
|
print(git_tag_define)
|
7
scripts/pre_script.py
Normal file
7
scripts/pre_script.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
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_TRACE_FACILITY=y
|
||||||
#CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
|
#CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
|
||||||
#CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=n
|
#CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=n
|
||||||
CONFIG_ESP_TLS_INSECURE=y
|
#CONFIG_ESP_TLS_INSECURE=y
|
||||||
CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
|
#CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
|
||||||
|
|
||||||
CONFIG_HEAP_CORRUPTION_DETECTION=CONFIG_HEAP_POISONING_LIGHT
|
CONFIG_HEAP_CORRUPTION_DETECTION=CONFIG_HEAP_POISONING_LIGHT
|
||||||
CONFIG_HEAP_POISONING_LIGHT=y
|
CONFIG_HEAP_POISONING_LIGHT=y
|
||||||
|
@ -16,18 +16,14 @@ CONFIG_HEAP_POISONING_LIGHT=y
|
||||||
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
|
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
|
||||||
CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
|
CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
|
||||||
CONFIG_BOOTLOADER_LOG_LEVEL=0
|
CONFIG_BOOTLOADER_LOG_LEVEL=0
|
||||||
CONFIG_LOG_BOOTLOADER_LEVEL_NONE=y
|
|
||||||
CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y
|
CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y
|
||||||
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
|
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
|
||||||
CONFIG_LOG_DEFAULT_LEVEL_NONE=y
|
CONFIG_LOG_DEFAULT_LEVEL_NONE=y
|
||||||
CONFIG_LOG_DEFAULT_LEVEL=0
|
CONFIG_LOG_DEFAULT_LEVEL=0
|
||||||
CONFIG_LOG_MAXIMUM_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_COMPILER_CXX_EXCEPTIONS=y
|
||||||
#CONFIG_BOOTLOADER_WDT_ENABLE=n
|
#CONFIG_BOOTLOADER_WDT_ENABLE=n
|
||||||
#CONFIG_TASK_WDT=n
|
#CONFIG_ESP_TASK_WDT=n
|
||||||
|
|
||||||
#Required for BTClock
|
#Required for BTClock
|
||||||
#CONFIG_SPIRAM_MODE_OCT=y
|
#CONFIG_SPIRAM_MODE_OCT=y
|
||||||
|
@ -42,12 +38,11 @@ CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=12
|
||||||
CONFIG_ESP32_WIFI_RX_BA_WIN=6
|
CONFIG_ESP32_WIFI_RX_BA_WIN=6
|
||||||
|
|
||||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3120
|
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3120
|
||||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240
|
|
||||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||||
CONFIG_RTC_CLK_CAL_CYCLES=576
|
CONFIG_RTC_CLK_CAL_CYCLES=576
|
||||||
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
|
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
|
||||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3120
|
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3120
|
||||||
CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n
|
CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n
|
||||||
CONFIG_SPIRAM_CACHE_WORKAROUND=y
|
CONFIG_SPIRAM_CACHE_WORKAROUND=y
|
||||||
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||||
CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
|
#CONFIG_NEWLIB_NANO_FORMAT=y
|
|
@ -1526,7 +1526,78 @@ const uint8_t Antonio_SemiBold40pt7bBitmaps[] PROGMEM = {
|
||||||
0x3F, 0xE0, 0x3F, 0xE0, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0x80, 0xFF, 0xFF,
|
0x3F, 0xE0, 0x3F, 0xE0, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0x80, 0xFF, 0xFF,
|
||||||
0xFE, 0x00, 0xFF, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 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,
|
0xC0, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xF8, 0x00, 0x00, 0x1F, 0x80,
|
||||||
0x00};
|
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
|
||||||
|
};
|
||||||
|
|
||||||
const GFXglyph Antonio_SemiBold40pt7bGlyphs[] PROGMEM = {
|
const GFXglyph Antonio_SemiBold40pt7bGlyphs[] PROGMEM = {
|
||||||
{0, 1, 1, 17, 0, 0}, // 0x20 ' '
|
{0, 1, 1, 17, 0, 0}, // 0x20 ' '
|
||||||
|
@ -1588,10 +1659,10 @@ const GFXglyph Antonio_SemiBold40pt7bGlyphs[] PROGMEM = {
|
||||||
{10963, 28, 67, 32, 2, -66}, // 0x58 'X'
|
{10963, 28, 67, 32, 2, -66}, // 0x58 'X'
|
||||||
{11198, 31, 67, 32, 1, -66}, // 0x59 'Y'
|
{11198, 31, 67, 32, 1, -66}, // 0x59 'Y'
|
||||||
{11458, 23, 67, 27, 3, -66}, // 0x5A 'Z'
|
{11458, 23, 67, 27, 3, -66}, // 0x5A 'Z'
|
||||||
{11651, 17, 70, 26, 6, -66}, // 0x5B '['
|
{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 '\'
|
{11800, 24, 67, 30, 3, -66}, // 0x5C '\'
|
||||||
{12001, 16, 70, 26, 4, -66}, // 0x5D ']'
|
{18557, 30, 68, 36, 3, -67 }, // 0x5D ']' --> pound { 0, 30, 68, 36, 3, -67 } was {12001, 16, 70, 26, 4, -66}
|
||||||
{12141, 29, 35, 37, 4, -66}, // 0x5E '^'
|
{18812, 31, 67, 32, 1, -66 }, // 0x5E '^' --> yen { 0, 31, 67, 32, 1, -66 } was {12141, 29, 35, 37, 4, -66
|
||||||
{12268, 25, 7, 29, 2, 2}, // 0x5F '_'
|
{12268, 25, 7, 29, 2, 2}, // 0x5F '_'
|
||||||
{12290, 12, 15, 16, 2, -77}, // 0x60 '`'
|
{12290, 12, 15, 16, 2, -77}, // 0x60 '`'
|
||||||
{12313, 27, 59, 36, 4, -57}, // 0x61 'a'
|
{12313, 27, 59, 36, 4, -57}, // 0x61 'a'
|
||||||
|
@ -1623,10 +1694,13 @@ const GFXglyph Antonio_SemiBold40pt7bGlyphs[] PROGMEM = {
|
||||||
{17509, 20, 75, 27, 4, -67}, // 0x7B '{'
|
{17509, 20, 75, 27, 4, -67}, // 0x7B '{'
|
||||||
{17697, 9, 75, 21, 6, -70}, // 0x7C '|'
|
{17697, 9, 75, 21, 6, -70}, // 0x7C '|'
|
||||||
{17782, 19, 75, 27, 4, -67}, // 0x7D '}'
|
{17782, 19, 75, 27, 4, -67}, // 0x7D '}'
|
||||||
{17961, 34, 14, 43, 4, -44}, {18021, 31, 69, 37, 2, -67}}; // 0x7E '~'
|
{17961, 34, 14, 43, 4, -44}}; // 0x7E '~'
|
||||||
|
|
||||||
|
|
||||||
|
//, {18021, 31, 69, 37, 2, -67}
|
||||||
|
|
||||||
const GFXfont Antonio_SemiBold40pt7b PROGMEM = {
|
const GFXfont Antonio_SemiBold40pt7b PROGMEM = {
|
||||||
(uint8_t *)Antonio_SemiBold40pt7bBitmaps,
|
(uint8_t *)Antonio_SemiBold40pt7bBitmaps,
|
||||||
(GFXglyph *)Antonio_SemiBold40pt7bGlyphs, 0x20, 0x7E, 101};
|
(GFXglyph *)Antonio_SemiBold40pt7bGlyphs, 0x20, 0x7E, 100};
|
||||||
|
|
||||||
// Approx. 18961 bytes
|
// Approx. 18961 bytes
|
||||||
|
|
|
@ -5029,7 +5029,231 @@ const uint8_t Antonio_SemiBold90pt7bBitmaps[] PROGMEM = {
|
||||||
0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF,
|
0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF,
|
||||||
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00,
|
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00,
|
||||||
0x00};
|
0x00,
|
||||||
|
|
||||||
|
// Pound sign (start 60325)
|
||||||
|
0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F,
|
||||||
|
0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xE0,
|
||||||
|
0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x03,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0x80, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00,
|
||||||
|
0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xF0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
|
||||||
|
0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xE0, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x0F, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0x80, 0x0F,
|
||||||
|
0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xF0, 0x03, 0xFF, 0xFF, 0xE0, 0x00,
|
||||||
|
0x0F, 0xFF, 0xFE, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0x00, 0xFF, 0xFF, 0xE0,
|
||||||
|
0x0F, 0xFF, 0xFE, 0x00, 0x00, 0x1F, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0xC0,
|
||||||
|
0x00, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xF8, 0x00, 0x00, 0x3F, 0xFF,
|
||||||
|
0xF0, 0x0F, 0xFF, 0xFE, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x01, 0xFF, 0xFF,
|
||||||
|
0xC0, 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0x3F, 0xFF, 0xF8, 0x00, 0x00, 0x0F,
|
||||||
|
0xFF, 0xFC, 0x07, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0xFF,
|
||||||
|
0xFF, 0xE0, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x1F, 0xFF, 0xF8, 0x00, 0x00,
|
||||||
|
0x07, 0xFF, 0xFE, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0,
|
||||||
|
0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x0F, 0xFF, 0xFC, 0x00,
|
||||||
|
0x00, 0x01, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x3F, 0xFF,
|
||||||
|
0xE0, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x07, 0xFF, 0xFC, 0x07, 0xFF, 0xFE,
|
||||||
|
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x1F,
|
||||||
|
0xFF, 0xF0, 0x1F, 0xFF, 0xFC, 0x00, 0x00, 0x03, 0xFF, 0xFE, 0x03, 0xFF,
|
||||||
|
0xFF, 0x80, 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0x7F, 0xFF, 0xF0, 0x00, 0x00,
|
||||||
|
0x0F, 0xFF, 0xF8, 0x0F, 0xFF, 0xFE, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x01,
|
||||||
|
0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x3F, 0xFF, 0xE0, 0x3F, 0xFF, 0xF8, 0x00,
|
||||||
|
0x00, 0x07, 0xFF, 0xFC, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xFF,
|
||||||
|
0x80, 0x7F, 0xFF, 0xF0, 0x00, 0x00, 0x1F, 0xFF, 0xF0, 0x0F, 0xFF, 0xFE,
|
||||||
|
0x00, 0x00, 0x03, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x7F,
|
||||||
|
0xFF, 0xC0, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF,
|
||||||
|
0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||||
|
0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFC, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF,
|
||||||
|
0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFC, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF,
|
||||||
|
0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
|
||||||
|
0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFE, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x7F,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x03, 0xFF, 0xFF,
|
||||||
|
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF,
|
||||||
|
0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x7F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFE, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF,
|
||||||
|
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF,
|
||||||
|
0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x7F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFE, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF,
|
||||||
|
0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||||
|
0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF8, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFC,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF,
|
||||||
|
0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x80, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F,
|
||||||
|
0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x80, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x3F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x80,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF,
|
||||||
|
0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF,
|
||||||
|
0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x80, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFE, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07,
|
||||||
|
0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
|
||||||
|
0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
|
||||||
|
|
||||||
|
// yen-sign (start 61607)
|
||||||
|
0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFE, 0xFF, 0xFF, 0xE0,
|
||||||
|
0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF9, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xE3,
|
||||||
|
0xFF, 0xFF, 0x80, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0x00,
|
||||||
|
0x00, 0x00, 0x07, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x1F,
|
||||||
|
0xFF, 0xFE, 0x0F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF8, 0x1F,
|
||||||
|
0xFF, 0xFC, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x3F, 0xFF, 0xFC, 0x00,
|
||||||
|
0x00, 0x00, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xF8, 0x00, 0x00, 0x03, 0xFF,
|
||||||
|
0xFF, 0xC0, 0x7F, 0xFF, 0xF0, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0xFF,
|
||||||
|
0xFF, 0xE0, 0x00, 0x00, 0x0F, 0xFF, 0xFE, 0x01, 0xFF, 0xFF, 0xE0, 0x00,
|
||||||
|
0x00, 0x1F, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x7F, 0xFF,
|
||||||
|
0xF8, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0xE0, 0x07, 0xFF,
|
||||||
|
0xFF, 0x80, 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0x00, 0x00,
|
||||||
|
0x03, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFE, 0x00, 0x00, 0x0F, 0xFF, 0xFE,
|
||||||
|
0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x3F, 0xFF,
|
||||||
|
0xFC, 0x00, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x00,
|
||||||
|
0x7F, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x01, 0xFF, 0xFF, 0xC0,
|
||||||
|
0x00, 0xFF, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF,
|
||||||
|
0xE0, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x0F,
|
||||||
|
0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0x3F, 0xFF, 0xF8, 0x00,
|
||||||
|
0x07, 0xFF, 0xFF, 0x80, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFF,
|
||||||
|
0x00, 0x00, 0xFF, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x01, 0xFF,
|
||||||
|
0xFF, 0x80, 0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00,
|
||||||
|
0x1F, 0xFF, 0xFC, 0x00, 0x0F, 0xFF, 0xFE, 0x00, 0x00, 0x3F, 0xFF, 0xF8,
|
||||||
|
0x00, 0x1F, 0xFF, 0xFC, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x3F, 0xFF,
|
||||||
|
0xF0, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00,
|
||||||
|
0xFF, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0xFF, 0xC0,
|
||||||
|
0x03, 0xFF, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFE,
|
||||||
|
0x00, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x07,
|
||||||
|
0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x00, 0x0F, 0xFF, 0xFE, 0x00,
|
||||||
|
0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0xFF, 0xFF, 0xC0,
|
||||||
|
0x00, 0x00, 0x1F, 0xFF, 0xFC, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x3F,
|
||||||
|
0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x0F,
|
||||||
|
0xFF, 0xFC, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x1F, 0xFF, 0xF8, 0x00,
|
||||||
|
0x00, 0x00, 0xFF, 0xFF, 0xE0, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x01, 0xFF,
|
||||||
|
0xFF, 0xC0, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x81, 0xFF,
|
||||||
|
0xFF, 0x80, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0x00, 0x00,
|
||||||
|
0x00, 0x07, 0xFF, 0xFF, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x0F, 0xFF,
|
||||||
|
0xFE, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFE, 0x3F, 0xFF,
|
||||||
|
0xF0, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFC, 0x7F, 0xFF, 0xE0, 0x00, 0x00,
|
||||||
|
0x00, 0x3F, 0xFF, 0xF8, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x3F, 0xFF,
|
||||||
|
0xF3, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF7, 0xFF, 0xFE,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xEF, 0xFF, 0xFC, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xFF, 0xFF, 0xDF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0,
|
||||||
|
0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00,
|
||||||
|
0x07, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xF8, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x3F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF,
|
||||||
|
0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xF0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0F,
|
||||||
|
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFE,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
|
||||||
|
0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xC0,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x01, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF,
|
||||||
|
0xFF, 0xFC, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFC, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xE0, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
|
||||||
|
0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x7F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
|
||||||
|
0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
|
||||||
|
0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xE0, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
|
||||||
|
0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x3F, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
|
||||||
|
0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00,
|
||||||
|
0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF8,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||||
|
0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x80,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F,
|
||||||
|
0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF8, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF,
|
||||||
|
0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x80, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF,
|
||||||
|
0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF,
|
||||||
|
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x80, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x1F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFC,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||||
|
0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xC0,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
const GFXglyph Antonio_SemiBold90pt7bGlyphs[] PROGMEM = {
|
const GFXglyph Antonio_SemiBold90pt7bGlyphs[] PROGMEM = {
|
||||||
{0, 1, 1, 38, 0, 0}, // 0x20 ' '
|
{0, 1, 1, 38, 0, 0}, // 0x20 ' '
|
||||||
|
@ -5090,11 +5314,16 @@ const GFXglyph Antonio_SemiBold90pt7bGlyphs[] PROGMEM = {
|
||||||
{53398, 108, 151, 116, 4, 106}, // 0x57 'W'
|
{53398, 108, 151, 116, 4, 106}, // 0x57 'W'
|
||||||
{55437, 64, 151, 72, 4, 106}, // 0x58 'X'
|
{55437, 64, 151, 72, 4, 106}, // 0x58 'X'
|
||||||
{56645, 71, 151, 73, 1, 106}, // 0x59 'Y'
|
{56645, 71, 151, 73, 1, 106}, // 0x59 'Y'
|
||||||
{57986, 52, 151, 61, 6, 106},
|
{57986, 52, 151, 61, 6, 106}, // 0x5A 'Z'
|
||||||
{58968, 70, 155, 84, 5, 104}}; // 0x5A 'Z'
|
{58968, 70, 155, 84, 5, 104}, // Euro-sign as [
|
||||||
|
{0, 0, 0, 0, 0, 0}, // Skip backslash
|
||||||
|
{60325, 67, 153, 82, 7, 104 }, // Pound-sign as ]
|
||||||
|
{61607, 71, 151, 73, 1, 106 }, // Yen-sign as ^
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
const GFXfont Antonio_SemiBold90pt7b PROGMEM = {
|
const GFXfont Antonio_SemiBold90pt7b PROGMEM = {
|
||||||
(uint8_t *)Antonio_SemiBold90pt7bBitmaps,
|
(uint8_t *)Antonio_SemiBold90pt7bBitmaps,
|
||||||
(GFXglyph *)Antonio_SemiBold90pt7bGlyphs, 0x20, 0x5B, 228};
|
(GFXglyph *)Antonio_SemiBold90pt7bGlyphs, 0x20, 0x5E, 231};
|
||||||
|
|
||||||
// Approx. 60745 bytes
|
// Approx. 60745 bytes
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "antonio-semibold20.h"
|
#include "antonio-semibold20.h"
|
||||||
#include "antonio-semibold30.h"
|
//#include "antonio-semibold30.h"
|
||||||
#include "antonio-semibold40.h"
|
#include "antonio-semibold40.h"
|
||||||
#include "antonio-semibold90.h"
|
#include "antonio-semibold90.h"
|
||||||
#include "sats-symbol.h"
|
#include "sats-symbol.h"
|
||||||
|
//#include "icons.h"
|
||||||
|
|
||||||
// #include "oswald-20.h"
|
// #include "oswald-20.h"
|
||||||
// #include "oswald-30.h"
|
// #include "oswald-30.h"
|
||||||
|
|
542
src/icons/icons.cpp
Normal file
542
src/icons/icons.cpp
Normal file
|
@ -0,0 +1,542 @@
|
||||||
|
#include "icons.h"
|
||||||
|
|
||||||
|
// 'lightning-bolt', 122x122px
|
||||||
|
const unsigned char epd_icons_lightning_bolt [] PROGMEM = {
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0
|
||||||
|
};
|
||||||
|
// 'rocket-launch', 122x122px
|
||||||
|
const unsigned char epd_icons_rocket_launch [] PROGMEM = {
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x07, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xfe, 0xf8, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xf1, 0xf0, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xc1, 0xf0, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0x03, 0xe0, 0x00, 0x00, 0x1f, 0xff, 0x80, 0x01, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xfc, 0x03, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xf0, 0x07, 0xc0, 0x00, 0x00, 0x07, 0xfe, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0x80, 0x0f, 0x80, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xfe, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xf8, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xe0, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x80, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x80, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xe0, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xf8, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xc0, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xfc, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xf0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xe0, 0x7e, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0x81, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0x03, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xfe, 0x07, 0xe7, 0xe0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xfc, 0x0f, 0xc3, 0xf0, 0x00, 0x00, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xf8, 0x1f, 0x81, 0xf8, 0x00, 0x03, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xf0, 0x3f, 0x00, 0xfc, 0x00, 0x0f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xe0, 0x7e, 0x00, 0x7e, 0x00, 0x3f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xc0, 0xfc, 0x00, 0x3f, 0x80, 0xff, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x81, 0xf8, 0x00, 0x7f, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x03, 0xf0, 0x00, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x07, 0xe0, 0x01, 0xf3, 0xff, 0xe0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x0f, 0xc0, 0x03, 0xe0, 0xff, 0x80, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x1f, 0x80, 0x07, 0xc0, 0xff, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x3f, 0x00, 0x0f, 0x81, 0xff, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x7e, 0x00, 0x1f, 0x03, 0xff, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xfc, 0x00, 0x3e, 0x07, 0xff, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xf8, 0x00, 0x7c, 0x0f, 0xff, 0x80, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xf0, 0x00, 0xf8, 0x1f, 0xff, 0xc0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xe0, 0x01, 0xf0, 0x3f, 0xff, 0xc0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xc0, 0x03, 0xe0, 0x7f, 0xff, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x80, 0x07, 0xc0, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x00, 0x0f, 0x81, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x00, 0x1f, 0x03, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x00, 0x3e, 0x07, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x00, 0x7c, 0x0f, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x00, 0xf8, 0x1f, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x01, 0xf0, 0x3f, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0
|
||||||
|
};
|
||||||
|
// 'pickaxe', 122x122px
|
||||||
|
const unsigned char epd_icons_pickaxe [] PROGMEM = {
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x31, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xe0, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x7f, 0xf8, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x3f, 0xfd, 0x80, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xf8, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x1f, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0x80, 0x1f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xfe, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xfe, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xfe, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xf0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xe0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0x80, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xc0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned char epd_icons_bitaxe_logo [] PROGMEM = {
|
||||||
|
// 'bitaxe_dark copy', 88x220px
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3e, 0xff, 0xff, 0xfd, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0xff, 0xff, 0xfc, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xfe,
|
||||||
|
0x00, 0xff, 0xff, 0xfe, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0xff, 0x00,
|
||||||
|
0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xf8, 0x00, 0x3f, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x1f, 0xff, 0xff,
|
||||||
|
0xf0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x0f, 0xff, 0xff, 0xf8, 0x03, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xf0, 0x00, 0x07, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x03, 0xff,
|
||||||
|
0xff, 0xfc, 0x00, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xff,
|
||||||
|
0xff, 0xff, 0xe0, 0x07, 0xf8, 0x7f, 0xff, 0xfd, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xe0, 0x07, 0xfe,
|
||||||
|
0x3f, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0x07,
|
||||||
|
0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xcf, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xff, 0x80, 0x0f,
|
||||||
|
0xff, 0xe7, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xff, 0x80, 0x1f, 0xff, 0xf3, 0xff, 0xff, 0xf0,
|
||||||
|
0x01, 0xff, 0xff, 0xff, 0xc0, 0x1f, 0xff, 0xf3, 0xff, 0xff, 0xf0, 0x03, 0xff, 0xff, 0xff, 0xf0,
|
||||||
|
0x1f, 0xff, 0xf9, 0xff, 0xff, 0xf0, 0x03, 0xff, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xfd, 0xff, 0xff,
|
||||||
|
0xe0, 0x07, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xfd, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xfc, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xfe,
|
||||||
|
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xfe, 0x7f,
|
||||||
|
0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xfc, 0x7f, 0xff, 0xff,
|
||||||
|
0xff, 0xfc, 0x00, 0x00, 0x7f, 0x3f, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x7e,
|
||||||
|
0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x0f, 0xff,
|
||||||
|
0xff, 0xff, 0xf0, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00,
|
||||||
|
0x3c, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03,
|
||||||
|
0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x80, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xf8,
|
||||||
|
0x1f, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xf0, 0x03, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x07,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80,
|
||||||
|
0x0f, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0x80, 0x1f, 0xff, 0xfc, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xfc, 0xff, 0xff, 0x00, 0x1f, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00,
|
||||||
|
0x00, 0x3f, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f,
|
||||||
|
0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00,
|
||||||
|
0x01, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xc0,
|
||||||
|
0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0xf8, 0x00,
|
||||||
|
0x00, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff,
|
||||||
|
0xe0, 0x0f, 0xff, 0xf1, 0xf8, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xf9, 0xfc,
|
||||||
|
0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0x3f, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xfd, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x3f,
|
||||||
|
0xff, 0xf8, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x1f, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xfc,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x01, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff,
|
||||||
|
0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff,
|
||||||
|
0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xf7, 0xff, 0xff, 0xfc, 0x3f, 0xff,
|
||||||
|
0xff, 0xff, 0xe0, 0x07, 0xff, 0xf3, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff,
|
||||||
|
0xf3, 0xff, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xf3, 0xff, 0xff, 0xf8, 0x01,
|
||||||
|
0xff, 0xff, 0xff, 0x80, 0x0f, 0xff, 0xf9, 0xff, 0xff, 0xf0, 0x01, 0xff, 0xff, 0xff, 0x80, 0x1f,
|
||||||
|
0xff, 0xf9, 0xff, 0xff, 0xf0, 0x03, 0xff, 0xff, 0xff, 0xc0, 0x1f, 0xff, 0xfc, 0xff, 0xff, 0xf0,
|
||||||
|
0x03, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xfe, 0x3f, 0xff, 0xe0, 0x03, 0xff, 0xff, 0xff, 0xf8,
|
||||||
|
0x3f, 0xff, 0x7e, 0x1f, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xfc, 0xff, 0x03, 0xff,
|
||||||
|
0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0x80, 0x01, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x80, 0x03, 0xff, 0xe0,
|
||||||
|
0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xe0, 0x00, 0x00, 0x1f, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0xff,
|
||||||
|
0xf8, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xf8, 0x00, 0x00, 0x3f, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xf8, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff,
|
||||||
|
0xff, 0xfc, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x7f,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff,
|
||||||
|
0xff, 0x83, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff,
|
||||||
|
0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||||
|
0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0x80,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xf8, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xf8, 0x7f,
|
||||||
|
0xff, 0xf9, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xf0, 0xff, 0xf8, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xe0, 0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x07, 0xff, 0xc0, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x80, 0x1f, 0xf0,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x80, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0xff, 0xc0, 0x3f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xe0, 0x3f,
|
||||||
|
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xf0, 0x7f, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x1f, 0xff, 0xf9, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff,
|
||||||
|
0xff, 0xf0, 0x1f, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xfd,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x1f, 0xff,
|
||||||
|
0xff, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x1f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff,
|
||||||
|
0xef, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xfc, 0x00,
|
||||||
|
0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00,
|
||||||
|
0x00, 0x07, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xf8,
|
||||||
|
0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x60, 0x00,
|
||||||
|
0x00, 0x00, 0x03, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff,
|
||||||
|
0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f,
|
||||||
|
0xff, 0xe0, 0x07, 0xff, 0xff, 0x80, 0x0f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xe0, 0x07, 0xff, 0xff,
|
||||||
|
0x80, 0x1f, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xc0, 0x07, 0xff, 0xff, 0x80, 0x1f, 0xff, 0xff, 0xf8,
|
||||||
|
0x0f, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xf8, 0x07, 0xff, 0x80, 0x0f, 0xff,
|
||||||
|
0xff, 0x00, 0x3f, 0xff, 0xff, 0xf8, 0x03, 0xff, 0x80, 0x1f, 0xff, 0xff, 0x80, 0x3f, 0xff, 0xff,
|
||||||
|
0xf0, 0x01, 0xff, 0xc0, 0x1f, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0xff, 0xf0, 0x01, 0xff, 0xf0, 0x1f,
|
||||||
|
0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xf0, 0x03, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xf8, 0x7f, 0xff,
|
||||||
|
0xff, 0xe0, 0x03, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xfe,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x07, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff,
|
||||||
|
0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f,
|
||||||
|
0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xe0, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x7f, 0xff, 0x80, 0x00, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0xfe, 0x00, 0x7f, 0xff, 0x80, 0x03, 0xff,
|
||||||
|
0xff, 0xff, 0xf8, 0xff, 0xfe, 0x00, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xfe,
|
||||||
|
0x00, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xfe, 0x00, 0xff, 0xff, 0xe0, 0x1f,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xfe, 0x01, 0xff, 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xfc,
|
||||||
|
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xfe, 0x03, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff,
|
||||||
|
0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff,
|
||||||
|
0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff,
|
||||||
|
0xbf, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xe0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0x9f,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x3f, 0xe7, 0xff, 0xff, 0xe7, 0xfd, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xfe, 0x00, 0x1f, 0xff, 0xff, 0xfc, 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff
|
||||||
|
};
|
||||||
|
|
||||||
|
// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 8032)
|
||||||
|
const int epd_icons_allArray_LEN = 4;
|
||||||
|
const unsigned char* epd_icons_allArray[epd_icons_allArray_LEN] = {
|
||||||
|
epd_icons_pickaxe,
|
||||||
|
epd_icons_rocket_launch,
|
||||||
|
epd_icons_lightning_bolt,
|
||||||
|
epd_icons_bitaxe_logo
|
||||||
|
};
|
10
src/icons/icons.h
Normal file
10
src/icons/icons.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef ICONS_H
|
||||||
|
#define ICONS_H
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
extern const unsigned char* epd_icons_allArray[];
|
||||||
|
|
||||||
|
#endif // ICONS_H
|
61
src/lib/bitaxe_fetch.cpp
Normal file
61
src/lib/bitaxe_fetch.cpp
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#include "bitaxe_fetch.hpp"
|
||||||
|
|
||||||
|
TaskHandle_t bitaxeFetchTaskHandle;
|
||||||
|
|
||||||
|
std::string bitaxeHashrate;
|
||||||
|
std::string bitaxeBestDiff;
|
||||||
|
|
||||||
|
std::string getBitAxeHashRate()
|
||||||
|
{
|
||||||
|
return bitaxeHashrate;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getBitaxeBestDiff()
|
||||||
|
{
|
||||||
|
return bitaxeBestDiff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void taskBitaxeFetch(void *pvParameters)
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
|
|
||||||
|
HTTPClient http;
|
||||||
|
http.setUserAgent(USER_AGENT);
|
||||||
|
String bitaxeApiUrl = "http://" + preferences.getString("bitaxeHostname", DEFAULT_BITAXE_HOSTNAME) + "/api/system/info";
|
||||||
|
http.begin(bitaxeApiUrl.c_str());
|
||||||
|
|
||||||
|
int httpCode = http.GET();
|
||||||
|
|
||||||
|
if (httpCode == 200)
|
||||||
|
{
|
||||||
|
String payload = http.getString();
|
||||||
|
JsonDocument doc;
|
||||||
|
deserializeJson(doc, payload);
|
||||||
|
bitaxeHashrate = std::to_string(static_cast<int>(std::round(doc["hashRate"].as<float>())));
|
||||||
|
bitaxeBestDiff = doc["bestDiff"].as<std::string>();
|
||||||
|
|
||||||
|
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BITAXE_HASHRATE || getCurrentScreen() == SCREEN_BITAXE_BESTDIFF))
|
||||||
|
{
|
||||||
|
WorkItem priceUpdate = {TASK_BITAXE_UPDATE, 0};
|
||||||
|
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.print(
|
||||||
|
F("Error retrieving BitAxe data. HTTP status code: "));
|
||||||
|
Serial.println(httpCode);
|
||||||
|
Serial.println(bitaxeApiUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupBitaxeFetchTask()
|
||||||
|
{
|
||||||
|
xTaskCreate(taskBitaxeFetch, "bitaxeFetch", (3 * 1024), NULL, tskIDLE_PRIORITY,
|
||||||
|
&bitaxeFetchTaskHandle);
|
||||||
|
|
||||||
|
xTaskNotifyGive(bitaxeFetchTaskHandle);
|
||||||
|
}
|
15
src/lib/bitaxe_fetch.hpp
Normal file
15
src/lib/bitaxe_fetch.hpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
|
||||||
|
#include "lib/config.hpp"
|
||||||
|
#include "lib/shared.hpp"
|
||||||
|
|
||||||
|
extern TaskHandle_t bitaxeFetchTaskHandle;
|
||||||
|
|
||||||
|
void setupBitaxeFetchTask();
|
||||||
|
void taskBitaxeFetch(void *pvParameters);
|
||||||
|
|
||||||
|
std::string getBitAxeHashRate();
|
||||||
|
std::string getBitaxeBestDiff();
|
|
@ -2,67 +2,62 @@
|
||||||
|
|
||||||
char *wsServer;
|
char *wsServer;
|
||||||
esp_websocket_client_handle_t blockNotifyClient = NULL;
|
esp_websocket_client_handle_t blockNotifyClient = NULL;
|
||||||
uint currentBlockHeight = 816000;
|
uint currentBlockHeight = 873400;
|
||||||
uint blockMedianFee = 1;
|
uint blockMedianFee = 1;
|
||||||
bool blockNotifyInit = false;
|
bool blockNotifyInit = false;
|
||||||
|
unsigned long int lastBlockUpdate;
|
||||||
|
|
||||||
// const char *mempoolWsCert = R"(-----BEGIN CERTIFICATE-----
|
const char *mempoolWsCert = R"EOF(
|
||||||
// MIIHfTCCBmWgAwIBAgIRANFX3mhqRYDt1NFuENoSyaAwDQYJKoZIhvcNAQELBQAw
|
-----BEGIN CERTIFICATE-----
|
||||||
// gZUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
|
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
|
||||||
// BgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDE9MDsGA1UE
|
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
|
||||||
// AxM0U2VjdGlnbyBSU0EgT3JnYW5pemF0aW9uIFZhbGlkYXRpb24gU2VjdXJlIFNl
|
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
|
||||||
// cnZlciBDQTAeFw0yMzA3MjQwMDAwMDBaFw0yNDA4MjIyMzU5NTlaMFcxCzAJBgNV
|
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
|
||||||
// BAYTAkpQMQ4wDAYDVQQIEwVUb2t5bzEgMB4GA1UEChMXTUVNUE9PTCBTUEFDRSBD
|
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
|
||||||
// Ty4sIExURC4xFjAUBgNVBAMTDW1lbXBvb2wuc3BhY2UwggEiMA0GCSqGSIb3DQEB
|
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
|
||||||
// AQUAA4IBDwAwggEKAoIBAQCqmiPRWgo58d25R0biQjAksXMq5ciH7z7ZQo2w2AbB
|
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
|
||||||
// rHxpnlIry74b9S4wRY5UJeYmd6ZwA76NdSioDvxTJc29bLplY+Ftmfc4ET0zYb2k
|
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
|
||||||
// Fi86z7GOWb6Ezor/qez9uMM9cxd021Bvcs0/2OrL6Sgp66u9keDZv9NyvFPpXfuR
|
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
|
||||||
// tdV2r4HF57VJqZn105PN4k80kNWgDbae8aw+BuUNvQYKEe71yfB7Bh6zSh9pCSfM
|
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
|
||||||
// I6pIJdQzoada2uY1dQMoJeIq8qKNKqAPKGsH5McemUT5ZIKU/tjk3nfX0pz/sQa4
|
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
|
||||||
// CN7tLH6UeUlctei92GFd6Xtn7RbKLhDUbc4Sq02Cc9iXAgMBAAGjggQDMIID/zAf
|
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
|
||||||
// BgNVHSMEGDAWgBQX2dYlJ2f5McJJQ9kwNkSMbKlP6zAdBgNVHQ4EFgQUXkxoddJ6
|
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
|
||||||
// rKobsbmDdtuCK1ywXuIwDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAwHQYD
|
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
|
||||||
// VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEoGA1UdIARDMEEwNQYMKwYBBAGy
|
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
|
||||||
// MQECAQMEMCUwIwYIKwYBBQUHAgEWF2h0dHBzOi8vc2VjdGlnby5jb20vQ1BTMAgG
|
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
|
||||||
// BmeBDAECAjBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLnNlY3RpZ28uY29t
|
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
|
||||||
// L1NlY3RpZ29SU0FPcmdhbml6YXRpb25WYWxpZGF0aW9uU2VjdXJlU2VydmVyQ0Eu
|
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
|
||||||
// Y3JsMIGKBggrBgEFBQcBAQR+MHwwVQYIKwYBBQUHMAKGSWh0dHA6Ly9jcnQuc2Vj
|
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
|
||||||
// dGlnby5jb20vU2VjdGlnb1JTQU9yZ2FuaXphdGlvblZhbGlkYXRpb25TZWN1cmVT
|
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
|
||||||
// ZXJ2ZXJDQS5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29t
|
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
|
||||||
// MIIBgAYKKwYBBAHWeQIEAgSCAXAEggFsAWoAdwB2/4g/Crb7lVHCYcz1h7o0tKTN
|
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
|
||||||
// uyncaEIKn+ZnTFo6dAAAAYmc9m/gAAAEAwBIMEYCIQD8XOozx411S/bnZambGjTB
|
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
|
||||||
// yTcr2fCmggUfQLSmqksD5gIhAIjiEMg0o1VSuQW31gWzfzL6idCkIZeSKN104cdp
|
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
|
||||||
// xa4SAHcA2ra/az+1tiKfm8K7XGvocJFxbLtRhIU0vaQ9MEjX+6sAAAGJnPZwPwAA
|
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
|
||||||
// BAMASDBGAiEA2sPTZTzvxewzQ8vk36+BWAKuJS7AvJ5W3clvfwCa8OUCIQC74ekT
|
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
|
||||||
// Ged2fqQE4sVy74aS6HRA2ihC9VLtNrASJx1YjQB2AO7N0GTV2xrOxVy3nbTNE6Iy
|
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
|
||||||
// h0Z8vOzew1FIWUZxH7WbAAABiZz2cA8AAAQDAEcwRQIgEklH7wYCFuuJIFUHX5PY
|
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
|
||||||
// /vZ3bDoxOp+061PT3caa+rICIQC0abgfGlBKiHxp47JZxnW3wcVqWdiYX4ViLm9H
|
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
|
||||||
// xfx4ljCBxgYDVR0RBIG+MIG7gg1tZW1wb29sLnNwYWNlghMqLmZtdC5tZW1wb29s
|
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
|
||||||
// LnNwYWNlghMqLmZyYS5tZW1wb29sLnNwYWNlgg8qLm1lbXBvb2wuc3BhY2WCEyou
|
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
|
||||||
// dGs3Lm1lbXBvb2wuc3BhY2WCEyoudmExLm1lbXBvb2wuc3BhY2WCDGJpc3EubWFy
|
jjxDah2nGN59PRbxYvnKkKj9
|
||||||
// a2V0c4IKYmlzcS5uaW5qYYIObGlxdWlkLm5ldHdvcmuCDGxpcXVpZC5wbGFjZYIN
|
-----END CERTIFICATE-----
|
||||||
// bWVtcG9vbC5uaW5qYTANBgkqhkiG9w0BAQsFAAOCAQEAFvOSRnlHDfq9C8acjZEG
|
)EOF";
|
||||||
// 5XIqjNYigyWyjOvx83of6Z3PBKkAZB5D/UHBPp+jBDJiEb/QXC7Z7Y7kpuvnoVib
|
|
||||||
// b4jDc0RjGEsxL+3F7cSw26m3wILJhhHooGZRmFY4GOAeCZtYCOTzJsiZvFpDoQjU
|
|
||||||
// hTBxtaps05z0Ly9/eYvkXnjnBNROZJVR+KYHlq4TIoGNc4q4KvpfHv2I/vhS2M1e
|
|
||||||
// bECNNPEyRxHGKdXXO3huocE7aVKpy+JDR6cWwDu6hpdc1j/SCDqdTDFQ7McHOrqA
|
|
||||||
// fpPh4FcfePMh7Mqxtg2pSs5pXPtiP0ZjLgxd7HbAXct8Y+/jGk+k3sx3SeYXVimr
|
|
||||||
// ew==
|
|
||||||
// -----END CERTIFICATE-----)";
|
|
||||||
|
|
||||||
void setupBlockNotify() {
|
|
||||||
// currentBlockHeight = preferences.getUInt("blockHeight", 816000);
|
|
||||||
|
|
||||||
|
void setupBlockNotify()
|
||||||
|
{
|
||||||
IPAddress result;
|
IPAddress result;
|
||||||
|
|
||||||
int dnsErr = -1;
|
int dnsErr = -1;
|
||||||
String mempoolInstance =
|
String mempoolInstance =
|
||||||
preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
||||||
|
|
||||||
while (dnsErr != 1) {
|
while (dnsErr != 1 && !strchr(mempoolInstance.c_str(), ':'))
|
||||||
|
{
|
||||||
dnsErr = WiFi.hostByName(mempoolInstance.c_str(), result);
|
dnsErr = WiFi.hostByName(mempoolInstance.c_str(), result);
|
||||||
|
|
||||||
if (dnsErr != 1) {
|
if (dnsErr != 1)
|
||||||
|
{
|
||||||
Serial.print(mempoolInstance);
|
Serial.print(mempoolInstance);
|
||||||
Serial.println(F("mempool DNS could not be resolved"));
|
Serial.println(F("mempool DNS could not be resolved"));
|
||||||
WiFi.reconnect();
|
WiFi.reconnect();
|
||||||
|
@ -71,41 +66,61 @@ void setupBlockNotify() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current block height through regular API
|
// Get current block height through regular API
|
||||||
HTTPClient *http = new HTTPClient();
|
int blockFetch = getBlockFetch();
|
||||||
http->begin("https://" + mempoolInstance + "/api/blocks/tip/height");
|
|
||||||
int httpCode = http->GET();
|
|
||||||
|
|
||||||
if (httpCode > 0 && httpCode == HTTP_CODE_OK) {
|
if (blockFetch > currentBlockHeight)
|
||||||
String blockHeightStr = http->getString();
|
currentBlockHeight = blockFetch;
|
||||||
currentBlockHeight = blockHeightStr.toInt();
|
|
||||||
// xTaskNotifyGive(blockUpdateTaskHandle);
|
if (currentBlockHeight != -1)
|
||||||
if (workQueue != nullptr) {
|
{
|
||||||
|
lastBlockUpdate = esp_timer_get_time() / 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workQueue != nullptr)
|
||||||
|
{
|
||||||
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
|
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
|
||||||
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE) && preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE))
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::strcpy(wsServer, String("wss://" + mempoolInstance +
|
// std::strcpy(wsServer, String("wss://" + mempoolInstance +
|
||||||
// "/api/v1/ws").c_str());
|
// "/api/v1/ws").c_str());
|
||||||
|
|
||||||
|
const String protocol = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE) ? "wss" : "ws";
|
||||||
|
|
||||||
|
String mempoolUri = protocol + "://" + preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE) + "/api/v1/ws";
|
||||||
|
|
||||||
esp_websocket_client_config_t config = {
|
esp_websocket_client_config_t config = {
|
||||||
.uri = "wss://mempool.space/api/v1/ws",
|
// .uri = "wss://mempool.space/api/v1/ws",
|
||||||
// .task_stack = (6*1024),
|
.task_stack = (6*1024),
|
||||||
// .cert_pem = mempoolWsCert,
|
.user_agent = USER_AGENT
|
||||||
.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);
|
blockNotifyClient = esp_websocket_client_init(&config);
|
||||||
esp_websocket_register_events(blockNotifyClient, WEBSOCKET_EVENT_ANY,
|
esp_websocket_register_events(blockNotifyClient, WEBSOCKET_EVENT_ANY,
|
||||||
onWebsocketEvent, blockNotifyClient);
|
onWebsocketBlockEvent, blockNotifyClient);
|
||||||
esp_websocket_client_start(blockNotifyClient);
|
esp_websocket_client_start(blockNotifyClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onWebsocketEvent(void *handler_args, esp_event_base_t base,
|
void onWebsocketBlockEvent(void *handler_args, esp_event_base_t base,
|
||||||
int32_t event_id, void *event_data) {
|
int32_t event_id, void *event_data)
|
||||||
|
{
|
||||||
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
|
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
|
||||||
const String sub = "{\"action\": \"want\", \"data\":[\"blocks\", \"mempool-blocks\"]}";
|
const String sub = "{\"action\": \"want\", \"data\":[\"blocks\", \"mempool-blocks\"]}";
|
||||||
switch (event_id) {
|
switch (event_id)
|
||||||
|
{
|
||||||
case WEBSOCKET_EVENT_CONNECTED:
|
case WEBSOCKET_EVENT_CONNECTED:
|
||||||
blockNotifyInit = true;
|
blockNotifyInit = true;
|
||||||
|
|
||||||
|
@ -113,13 +128,14 @@ void onWebsocketEvent(void *handler_args, esp_event_base_t base,
|
||||||
|
|
||||||
Serial.println(sub);
|
Serial.println(sub);
|
||||||
if (esp_websocket_client_send_text(blockNotifyClient, sub.c_str(),
|
if (esp_websocket_client_send_text(blockNotifyClient, sub.c_str(),
|
||||||
sub.length(), portMAX_DELAY) == -1) {
|
sub.length(), portMAX_DELAY) == -1)
|
||||||
|
{
|
||||||
Serial.println(F("Mempool.space WS Block Subscribe Error"));
|
Serial.println(F("Mempool.space WS Block Subscribe Error"));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case WEBSOCKET_EVENT_DATA:
|
case WEBSOCKET_EVENT_DATA:
|
||||||
onWebsocketMessage(data);
|
onWebsocketBlockMessage(data);
|
||||||
break;
|
break;
|
||||||
case WEBSOCKET_EVENT_ERROR:
|
case WEBSOCKET_EVENT_ERROR:
|
||||||
Serial.println(F("Mempool.space WS Connnection error"));
|
Serial.println(F("Mempool.space WS Connnection error"));
|
||||||
|
@ -130,15 +146,15 @@ void onWebsocketEvent(void *handler_args, esp_event_base_t base,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onWebsocketMessage(esp_websocket_event_data_t *event_data) {
|
void onWebsocketBlockMessage(esp_websocket_event_data_t *event_data)
|
||||||
|
{
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
|
|
||||||
|
|
||||||
JsonDocument filter;
|
JsonDocument filter;
|
||||||
filter["block"]["height"] = true;
|
filter["block"]["height"] = true;
|
||||||
filter["mempool-blocks"][0]["medianFee"] = true;
|
filter["mempool-blocks"][0]["medianFee"] = true;
|
||||||
|
|
||||||
DeserializationError error = deserializeJson(doc, (char *)event_data->data_ptr, DeserializationOption::Filter(filter));
|
deserializeJson(doc, (char *)event_data->data_ptr, DeserializationOption::Filter(filter));
|
||||||
|
|
||||||
// if (error) {
|
// if (error) {
|
||||||
// Serial.print("deserializeJson() failed: ");
|
// Serial.print("deserializeJson() failed: ");
|
||||||
|
@ -146,88 +162,169 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data) {
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (doc.containsKey("block")) {
|
if (doc.containsKey("block"))
|
||||||
|
{
|
||||||
JsonObject block = doc["block"];
|
JsonObject block = doc["block"];
|
||||||
|
|
||||||
currentBlockHeight = block["height"].as<uint>();
|
if (block["height"].as<uint>() == currentBlockHeight) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
processNewBlock(block["height"].as<uint>());
|
||||||
|
}
|
||||||
|
else if (doc.containsKey("mempool-blocks"))
|
||||||
|
{
|
||||||
|
JsonArray blockInfo = doc["mempool-blocks"].as<JsonArray>();
|
||||||
|
|
||||||
|
uint medianFee = (uint)round(blockInfo[0]["medianFee"].as<double>());
|
||||||
|
|
||||||
|
processNewBlockFee(medianFee);
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void processNewBlock(uint newBlockHeight) {
|
||||||
|
if (newBlockHeight < currentBlockHeight)
|
||||||
|
return;
|
||||||
|
|
||||||
|
currentBlockHeight = newBlockHeight;
|
||||||
|
|
||||||
// Serial.printf("New block found: %d\r\n", block["height"].as<uint>());
|
// Serial.printf("New block found: %d\r\n", block["height"].as<uint>());
|
||||||
preferences.putUInt("blockHeight", currentBlockHeight);
|
preferences.putUInt("blockHeight", currentBlockHeight);
|
||||||
|
lastBlockUpdate = esp_timer_get_time() / 1000000;
|
||||||
|
|
||||||
if (workQueue != nullptr) {
|
if (workQueue != nullptr)
|
||||||
|
{
|
||||||
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
|
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
|
||||||
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
||||||
// xTaskNotifyGive(blockUpdateTaskHandle);
|
// xTaskNotifyGive(blockUpdateTaskHandle);
|
||||||
|
|
||||||
if (getCurrentScreen() != SCREEN_BLOCK_HEIGHT &&
|
if (getCurrentScreen() != SCREEN_BLOCK_HEIGHT &&
|
||||||
preferences.getBool("stealFocus", true)) {
|
preferences.getBool("stealFocus", DEFAULT_STEAL_FOCUS))
|
||||||
|
{
|
||||||
uint64_t timerPeriod = 0;
|
uint64_t timerPeriod = 0;
|
||||||
if (isTimerActive()) {
|
if (isTimerActive())
|
||||||
|
{
|
||||||
// store timer periode before making inactive to prevent artifacts
|
// store timer periode before making inactive to prevent artifacts
|
||||||
timerPeriod = getTimerSeconds();
|
timerPeriod = getTimerSeconds();
|
||||||
esp_timer_stop(screenRotateTimer);
|
esp_timer_stop(screenRotateTimer);
|
||||||
}
|
}
|
||||||
setCurrentScreen(SCREEN_BLOCK_HEIGHT);
|
setCurrentScreen(SCREEN_BLOCK_HEIGHT);
|
||||||
if (timerPeriod > 0) {
|
if (timerPeriod > 0)
|
||||||
|
{
|
||||||
esp_timer_start_periodic(screenRotateTimer,
|
esp_timer_start_periodic(screenRotateTimer,
|
||||||
timerPeriod * usPerSecond);
|
timerPeriod * usPerSecond);
|
||||||
}
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(315*NUM_SCREENS)); // Extra delay because of screen switching
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preferences.getBool("ledFlashOnUpd", false)) {
|
if (preferences.getBool("ledFlashOnUpd", DEFAULT_LED_FLASH_ON_UPD))
|
||||||
|
{
|
||||||
vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated
|
vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated
|
||||||
queueLedEffect(LED_FLASH_BLOCK_NOTIFY);
|
queueLedEffect(LED_FLASH_BLOCK_NOTIFY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (doc.containsKey("mempool-blocks")) {
|
}
|
||||||
JsonArray blockInfo = doc["mempool-blocks"].as<JsonArray>();
|
|
||||||
|
|
||||||
uint medianFee = (uint)round(blockInfo[0]["medianFee"].as<double>());
|
void processNewBlockFee(uint newBlockFee) {
|
||||||
|
if (blockMedianFee == newBlockFee)
|
||||||
if (blockMedianFee == medianFee) {
|
{
|
||||||
doc.clear();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serial.printf("New median fee: %d\r\n", medianFee);
|
// Serial.printf("New median fee: %d\r\n", medianFee);
|
||||||
blockMedianFee = medianFee;
|
blockMedianFee = newBlockFee;
|
||||||
|
|
||||||
if (workQueue != nullptr) {
|
if (workQueue != nullptr)
|
||||||
|
{
|
||||||
WorkItem blockUpdate = {TASK_FEE_UPDATE, 0};
|
WorkItem blockUpdate = {TASK_FEE_UPDATE, 0};
|
||||||
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint getBlockHeight() { return currentBlockHeight; }
|
uint getBlockHeight() { return currentBlockHeight; }
|
||||||
|
|
||||||
void setBlockHeight(uint newBlockHeight) {
|
void setBlockHeight(uint newBlockHeight)
|
||||||
|
{
|
||||||
currentBlockHeight = newBlockHeight;
|
currentBlockHeight = newBlockHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint getBlockMedianFee() { return blockMedianFee; }
|
uint getBlockMedianFee() { return blockMedianFee; }
|
||||||
|
|
||||||
void setBlockMedianFee(uint newBlockMedianFee) {
|
void setBlockMedianFee(uint newBlockMedianFee)
|
||||||
|
{
|
||||||
blockMedianFee = newBlockMedianFee;
|
blockMedianFee = newBlockMedianFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isBlockNotifyConnected() {
|
bool isBlockNotifyConnected()
|
||||||
if (blockNotifyClient == NULL) return false;
|
{
|
||||||
|
if (blockNotifyClient == NULL)
|
||||||
|
return false;
|
||||||
return esp_websocket_client_is_connected(blockNotifyClient);
|
return esp_websocket_client_is_connected(blockNotifyClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getBlockNotifyInit() {
|
bool getBlockNotifyInit()
|
||||||
|
{
|
||||||
return blockNotifyInit;
|
return blockNotifyInit;
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopBlockNotify() {
|
void stopBlockNotify()
|
||||||
if (blockNotifyClient == NULL) return;
|
{
|
||||||
|
if (blockNotifyClient == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
esp_websocket_client_close(blockNotifyClient, portMAX_DELAY);
|
esp_websocket_client_close(blockNotifyClient, pdMS_TO_TICKS(5000));
|
||||||
esp_websocket_client_stop(blockNotifyClient);
|
esp_websocket_client_stop(blockNotifyClient);
|
||||||
esp_websocket_client_destroy(blockNotifyClient);
|
esp_websocket_client_destroy(blockNotifyClient);
|
||||||
|
|
||||||
blockNotifyClient = NULL;
|
blockNotifyClient = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void restartBlockNotify()
|
||||||
|
{
|
||||||
|
stopBlockNotify();
|
||||||
|
|
||||||
|
if (blockNotifyClient == NULL) {
|
||||||
|
setupBlockNotify();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// esp_websocket_client_close(blockNotifyClient, pdMS_TO_TICKS(5000));
|
||||||
|
// esp_websocket_client_stop(blockNotifyClient);
|
||||||
|
// esp_websocket_client_start(blockNotifyClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
HTTPClient* http = HttpHelper::begin(url);
|
||||||
|
Serial.println("Fetching block height from " + url);
|
||||||
|
int httpCode = http->GET();
|
||||||
|
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
return 2203; // B-T-C
|
||||||
|
}
|
||||||
|
|
||||||
|
uint getLastBlockUpdate()
|
||||||
|
{
|
||||||
|
return lastBlockUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLastBlockUpdate(uint lastUpdate)
|
||||||
|
{
|
||||||
|
lastBlockUpdate = lastUpdate;
|
||||||
|
}
|
|
@ -11,15 +11,16 @@
|
||||||
|
|
||||||
#include "lib/led_handler.hpp"
|
#include "lib/led_handler.hpp"
|
||||||
#include "lib/screen_handler.hpp"
|
#include "lib/screen_handler.hpp"
|
||||||
|
#include "lib/timers.hpp"
|
||||||
#include "lib/shared.hpp"
|
#include "lib/shared.hpp"
|
||||||
|
|
||||||
// using namespace websockets;
|
// using namespace websockets;
|
||||||
|
|
||||||
void setupBlockNotify();
|
void setupBlockNotify();
|
||||||
|
|
||||||
void onWebsocketEvent(void *handler_args, esp_event_base_t base,
|
void onWebsocketBlockEvent(void *handler_args, esp_event_base_t base,
|
||||||
int32_t event_id, void *event_data);
|
int32_t event_id, void *event_data);
|
||||||
void onWebsocketMessage(esp_websocket_event_data_t *event_data);
|
void onWebsocketBlockMessage(esp_websocket_event_data_t *event_data);
|
||||||
|
|
||||||
void setBlockHeight(uint newBlockHeight);
|
void setBlockHeight(uint newBlockHeight);
|
||||||
uint getBlockHeight();
|
uint getBlockHeight();
|
||||||
|
@ -29,4 +30,12 @@ uint getBlockMedianFee();
|
||||||
|
|
||||||
bool isBlockNotifyConnected();
|
bool isBlockNotifyConnected();
|
||||||
void stopBlockNotify();
|
void stopBlockNotify();
|
||||||
|
void restartBlockNotify();
|
||||||
|
|
||||||
|
void processNewBlock(uint newBlockHeight);
|
||||||
|
void processNewBlockFee(uint newBlockFee);
|
||||||
|
|
||||||
bool getBlockNotifyInit();
|
bool getBlockNotifyInit();
|
||||||
|
uint getLastBlockUpdate();
|
||||||
|
int getBlockFetch();
|
||||||
|
void setLastBlockUpdate(uint lastUpdate);
|
|
@ -4,43 +4,67 @@ TaskHandle_t buttonTaskHandle = NULL;
|
||||||
const TickType_t debounceDelay = pdMS_TO_TICKS(50);
|
const TickType_t debounceDelay = pdMS_TO_TICKS(50);
|
||||||
TickType_t lastDebounceTime = 0;
|
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) {
|
void buttonTask(void *parameter) {
|
||||||
while (1) {
|
while (1) {
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
std::lock_guard<std::mutex> lock(mcpMutex);
|
|
||||||
|
|
||||||
TickType_t currentTime = xTaskGetTickCount();
|
TickType_t currentTime = xTaskGetTickCount();
|
||||||
|
|
||||||
if ((currentTime - lastDebounceTime) >= debounceDelay) {
|
if ((currentTime - lastDebounceTime) >= debounceDelay) {
|
||||||
lastDebounceTime = currentTime;
|
lastDebounceTime = currentTime;
|
||||||
|
|
||||||
if (!digitalRead(MCP_INT_PIN)) {
|
std::lock_guard<std::mutex> lock(mcpMutex);
|
||||||
uint pin = mcp1.getLastInterruptPin();
|
|
||||||
|
|
||||||
switch (pin) {
|
if (!digitalRead(MCP_INT_PIN)) {
|
||||||
case 3:
|
uint16_t intFlags = mcp1.getInterruptFlagRegister();
|
||||||
toggleTimerActive();
|
uint16_t intCap = mcp1.getInterruptCaptureRegister();
|
||||||
break;
|
|
||||||
case 2:
|
// Check each button individually
|
||||||
nextScreen();
|
if (intFlags & BTN_1) handleButton1();
|
||||||
break;
|
if (intFlags & BTN_2) handleButton2();
|
||||||
case 1:
|
if (intFlags & BTN_3) handleButton3();
|
||||||
previousScreen();
|
if (intFlags & BTN_4) handleButton4();
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
showSystemStatusScreen();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mcp1.clearInterrupts();
|
|
||||||
} else {
|
// Clear interrupt state
|
||||||
}
|
|
||||||
// Very ugly, but for some reason this is necessary
|
|
||||||
while (!digitalRead(MCP_INT_PIN)) {
|
while (!digitalRead(MCP_INT_PIN)) {
|
||||||
mcp1.clearInterrupts();
|
std::lock_guard<std::mutex> lock(mcpMutex);
|
||||||
|
mcp1.getInterruptCaptureRegister();
|
||||||
|
delay(1); // Small delay to prevent tight loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper functions to handle each button
|
||||||
|
void handleButton1() {
|
||||||
|
toggleTimerActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleButton2() {
|
||||||
|
nextScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleButton3() {
|
||||||
|
previousScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleButton4() {
|
||||||
|
showSystemStatusScreen();
|
||||||
|
}
|
||||||
|
|
||||||
void IRAM_ATTR handleButtonInterrupt() {
|
void IRAM_ATTR handleButtonInterrupt() {
|
||||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||||
xTaskNotifyFromISR(buttonTaskHandle, 0, eNoAction, &xHigherPriorityTaskWoken);
|
xTaskNotifyFromISR(buttonTaskHandle, 0, eNoAction, &xHigherPriorityTaskWoken);
|
||||||
|
@ -50,7 +74,7 @@ void IRAM_ATTR handleButtonInterrupt() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupButtonTask() {
|
void setupButtonTask() {
|
||||||
xTaskCreate(buttonTask, "ButtonTask", 4096, NULL, tskIDLE_PRIORITY,
|
xTaskCreate(buttonTask, "ButtonTask", 3072, NULL, tskIDLE_PRIORITY,
|
||||||
&buttonTaskHandle); // Create the FreeRTOS task
|
&buttonTaskHandle); // Create the FreeRTOS task
|
||||||
// Use interrupt instead of task
|
// Use interrupt instead of task
|
||||||
attachInterrupt(MCP_INT_PIN, handleButtonInterrupt, CHANGE);
|
attachInterrupt(MCP_INT_PIN, handleButtonInterrupt, CHANGE);
|
||||||
|
|
|
@ -4,9 +4,17 @@
|
||||||
|
|
||||||
#include "lib/screen_handler.hpp"
|
#include "lib/screen_handler.hpp"
|
||||||
#include "lib/shared.hpp"
|
#include "lib/shared.hpp"
|
||||||
|
#include "lib/timers.hpp"
|
||||||
|
|
||||||
extern TaskHandle_t buttonTaskHandle;
|
extern TaskHandle_t buttonTaskHandle;
|
||||||
|
|
||||||
|
// Task and setup functions
|
||||||
void buttonTask(void *pvParameters);
|
void buttonTask(void *pvParameters);
|
||||||
void IRAM_ATTR handleButtonInterrupt();
|
void IRAM_ATTR handleButtonInterrupt();
|
||||||
void setupButtonTask();
|
void setupButtonTask();
|
||||||
|
|
||||||
|
// Individual button handlers
|
||||||
|
void handleButton1();
|
||||||
|
void handleButton2();
|
||||||
|
void handleButton3();
|
||||||
|
void handleButton4();
|
||||||
|
|
|
@ -2,26 +2,42 @@
|
||||||
|
|
||||||
#define MAX_ATTEMPTS_WIFI_CONNECTION 20
|
#define MAX_ATTEMPTS_WIFI_CONNECTION 20
|
||||||
|
|
||||||
|
// zlib_turbo zt;
|
||||||
|
|
||||||
Preferences preferences;
|
Preferences preferences;
|
||||||
Adafruit_MCP23X17 mcp1;
|
MCP23017 mcp1(0x20);
|
||||||
#ifdef IS_BTCLOCK_S3
|
#ifdef IS_BTCLOCK_V8
|
||||||
Adafruit_MCP23X17 mcp2;
|
MCP23017 mcp2(0x21);
|
||||||
#endif
|
#endif
|
||||||
std::vector<std::string> screenNameMap(SCREEN_COUNT);
|
|
||||||
|
#ifdef HAS_FRONTLIGHT
|
||||||
|
PCA9685 flArray(PCA_I2C_ADDR);
|
||||||
|
BH1750 bh1750;
|
||||||
|
bool hasLuxSensor = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::vector<ScreenMapping> screenMappings;
|
||||||
std::mutex mcpMutex;
|
std::mutex mcpMutex;
|
||||||
|
uint lastTimeSync;
|
||||||
|
|
||||||
|
void addScreenMapping(int value, const char *name)
|
||||||
|
{
|
||||||
|
screenMappings.push_back({value, name});
|
||||||
|
}
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
setupPreferences();
|
setupPreferences();
|
||||||
setupHardware();
|
setupHardware();
|
||||||
|
|
||||||
setupDisplays();
|
setupDisplays();
|
||||||
if (preferences.getBool("ledTestOnPower", true))
|
if (preferences.getBool("ledTestOnPower", DEFAULT_LED_TEST_ON_POWER))
|
||||||
{
|
{
|
||||||
queueLedEffect(LED_POWER_TEST);
|
queueLedEffect(LED_POWER_TEST);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lockMcp(mcpMutex);
|
std::lock_guard<std::mutex> lockMcp(mcpMutex);
|
||||||
if (mcp1.digitalRead(3) == LOW)
|
if (mcp1.read1(3) == LOW)
|
||||||
{
|
{
|
||||||
preferences.putBool("wifiConfigured", false);
|
preferences.putBool("wifiConfigured", false);
|
||||||
preferences.remove("txPower");
|
preferences.remove("txPower");
|
||||||
|
@ -32,64 +48,100 @@ void setup()
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
if (mcp1.digitalRead(0) == LOW)
|
if (mcp1.read1(0) == LOW)
|
||||||
{
|
{
|
||||||
// Then loop forever to prevent anything else from writing to the screen
|
// Then loop forever to prevent anything else from writing to the screen
|
||||||
while (true) {
|
while (true)
|
||||||
|
{
|
||||||
delay(1000);
|
delay(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (mcp1.read1(1) == LOW)
|
||||||
|
{
|
||||||
|
preferences.clear();
|
||||||
|
queueLedEffect(LED_EFFECT_WIFI_ERASE_SETTINGS);
|
||||||
|
nvs_flash_erase();
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tryImprovSetup();
|
setupWifi();
|
||||||
|
// loadIcons();
|
||||||
|
|
||||||
setupWebserver();
|
setupWebserver();
|
||||||
|
|
||||||
// setupWifi();
|
syncTime();
|
||||||
setupTime();
|
|
||||||
finishSetup();
|
finishSetup();
|
||||||
|
|
||||||
setupTasks();
|
setupTasks();
|
||||||
setupTimers();
|
setupTimers();
|
||||||
|
|
||||||
xTaskCreate(setupWebsocketClients, "setupWebsocketClients", 4096, NULL,
|
if (preferences.getBool("useNostr", DEFAULT_USE_NOSTR) || preferences.getBool("nostrZapNotify", DEFAULT_ZAP_NOTIFY_ENABLED))
|
||||||
|
{
|
||||||
|
setupNostrNotify(preferences.getBool("useNostr", DEFAULT_USE_NOSTR), preferences.getBool("nostrZapNotify", DEFAULT_ZAP_NOTIFY_ENABLED));
|
||||||
|
setupNostrTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preferences.getBool("useNostr", DEFAULT_USE_NOSTR))
|
||||||
|
{
|
||||||
|
xTaskCreate(setupWebsocketClients, "setupWebsocketClients", 8192, NULL,
|
||||||
tskIDLE_PRIORITY, NULL);
|
tskIDLE_PRIORITY, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preferences.getBool("bitaxeEnabled", DEFAULT_BITAXE_ENABLED))
|
||||||
|
{
|
||||||
|
setupBitaxeFetchTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preferences.getBool("miningPoolStats", DEFAULT_MINING_POOL_STATS_ENABLED))
|
||||||
|
{
|
||||||
|
setupMiningPoolStatsFetchTask();
|
||||||
|
}
|
||||||
|
|
||||||
setupButtonTask();
|
setupButtonTask();
|
||||||
setupOTA();
|
setupOTA();
|
||||||
|
|
||||||
waitUntilNoneBusy();
|
waitUntilNoneBusy();
|
||||||
|
|
||||||
|
#ifdef HAS_FRONTLIGHT
|
||||||
|
if (!preferences.getBool("flAlwaysOn", DEFAULT_FL_ALWAYS_ON))
|
||||||
|
{
|
||||||
|
frontlightFadeOutAll(preferences.getUInt("flEffectDelay"), true);
|
||||||
|
flArray.allOFF();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
forceFullRefresh();
|
forceFullRefresh();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tryImprovSetup()
|
void setupWifi()
|
||||||
{
|
{
|
||||||
WiFi.onEvent(WiFiEvent);
|
WiFi.onEvent(WiFiEvent);
|
||||||
WiFi.setAutoConnect(true);
|
WiFi.setAutoConnect(true);
|
||||||
WiFi.setAutoReconnect(true);
|
WiFi.setAutoReconnect(true);
|
||||||
WiFi.begin();
|
WiFi.begin();
|
||||||
if (preferences.getInt("txPower", 0))
|
if (preferences.getInt("txPower", DEFAULT_TX_POWER))
|
||||||
{
|
{
|
||||||
if (WiFi.setTxPower(
|
if (WiFi.setTxPower(
|
||||||
static_cast<wifi_power_t>(preferences.getInt("txPower", 0))))
|
static_cast<wifi_power_t>(preferences.getInt("txPower", DEFAULT_TX_POWER))))
|
||||||
{
|
{
|
||||||
Serial.printf("WiFi max tx power set to %d\n",
|
Serial.printf("WiFi max tx power set to %d\n",
|
||||||
preferences.getInt("txPower", 0));
|
preferences.getInt("txPower", DEFAULT_TX_POWER));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (!preferences.getBool("wifiConfigured", false))
|
// if (!preferences.getBool("wifiConfigured", DEFAULT_WIFI_CONFIGURED)
|
||||||
{
|
{
|
||||||
|
|
||||||
queueLedEffect(LED_EFFECT_WIFI_WAIT_FOR_CONFIG);
|
queueLedEffect(LED_EFFECT_WIFI_WAIT_FOR_CONFIG);
|
||||||
|
|
||||||
uint8_t x_buffer[16];
|
|
||||||
uint8_t x_position = 0;
|
|
||||||
|
|
||||||
bool buttonPress = false;
|
bool buttonPress = false;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lockMcp(mcpMutex);
|
std::lock_guard<std::mutex> lockMcp(mcpMutex);
|
||||||
buttonPress = (mcp1.digitalRead(2) == LOW);
|
buttonPress = (mcp1.read1(2) == LOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -100,22 +152,22 @@ void tryImprovSetup()
|
||||||
String softAP_SSID =
|
String softAP_SSID =
|
||||||
String("BTClock" + String(mac[5], 16) + String(mac[1], 16));
|
String("BTClock" + String(mac[5], 16) + String(mac[1], 16));
|
||||||
WiFi.setHostname(softAP_SSID.c_str());
|
WiFi.setHostname(softAP_SSID.c_str());
|
||||||
String softAP_password =
|
String softAP_password = replaceAmbiguousChars(
|
||||||
base64::encode(String(mac[2], 16) + String(mac[4], 16) +
|
base64::encode(String(mac[2], 16) + String(mac[4], 16) +
|
||||||
String(mac[5], 16) + String(mac[1], 16))
|
String(mac[5], 16) + String(mac[1], 16) + String(mac[3], 16))
|
||||||
.substring(2, 10);
|
.substring(2, 10));
|
||||||
|
|
||||||
// wm.setConfigPortalTimeout(preferences.getUInt("wpTimeout", 600));
|
wm.setConfigPortalTimeout(preferences.getUInt("wpTimeout", DEFAULT_WP_TIMEOUT));
|
||||||
wm.setWiFiAutoReconnect(false);
|
wm.setWiFiAutoReconnect(false);
|
||||||
wm.setDebugOutput(false);
|
wm.setDebugOutput(false);
|
||||||
wm.setConfigPortalBlocking(true);
|
wm.setConfigPortalBlocking(true);
|
||||||
|
|
||||||
wm.setAPCallback([&](WiFiManager *wifiManager)
|
wm.setAPCallback([&](WiFiManager *wifiManager)
|
||||||
{
|
{
|
||||||
// Serial.printf("Entered config mode:ip=%s, ssid='%s', pass='%s'\n",
|
Serial.printf("Entered config mode:ip=%s, ssid='%s', pass='%s'\n",
|
||||||
// WiFi.softAPIP().toString().c_str(),
|
WiFi.softAPIP().toString().c_str(),
|
||||||
// wifiManager->getConfigPortalSSID().c_str(),
|
wifiManager->getConfigPortalSSID().c_str(),
|
||||||
// softAP_password.c_str());
|
softAP_password.c_str());
|
||||||
// delay(6000);
|
// delay(6000);
|
||||||
setFgColor(GxEPD_BLACK);
|
setFgColor(GxEPD_BLACK);
|
||||||
setBgColor(GxEPD_WHITE);
|
setBgColor(GxEPD_WHITE);
|
||||||
|
@ -124,13 +176,23 @@ void tryImprovSetup()
|
||||||
const String explainText = "*SSID: *\r\n" +
|
const String explainText = "*SSID: *\r\n" +
|
||||||
wifiManager->getConfigPortalSSID() +
|
wifiManager->getConfigPortalSSID() +
|
||||||
"\r\n\r\n*Password:*\r\n" + softAP_password;
|
"\r\n\r\n*Password:*\r\n" + softAP_password;
|
||||||
|
// Set the UNIX timestamp
|
||||||
|
time_t timestamp = LAST_BUILD_TIME; // Example timestamp: March 7, 2021 00:00:00 UTC
|
||||||
|
|
||||||
|
// Convert the timestamp to a struct tm in UTC
|
||||||
|
struct tm *timeinfo = gmtime(×tamp);
|
||||||
|
|
||||||
|
// Format the date
|
||||||
|
char formattedDate[20];
|
||||||
|
strftime(formattedDate, sizeof(formattedDate), "%y-%m-%d\r\n%H:%M:%S", timeinfo);
|
||||||
|
|
||||||
std::array<String, NUM_SCREENS> epdContent = {
|
std::array<String, NUM_SCREENS> epdContent = {
|
||||||
"Welcome!",
|
"Welcome!",
|
||||||
"Bienvenidos!",
|
"Bienvenidos!",
|
||||||
"To setup\r\nscan QR or\r\nconnect\r\nmanually",
|
"To setup\r\nscan QR or\r\nconnect\r\nmanually",
|
||||||
"Para\r\nconfigurar\r\nescanear QR\r\no conectar\r\nmanualmente",
|
"Para\r\nconfigurar\r\nescanear QR\r\no conectar\r\nmanualmente",
|
||||||
explainText,
|
explainText,
|
||||||
"*Hostname*:\r\n" + getMyHostname(),
|
"*Hostname*:\r\n" + getMyHostname() + "\r\n\r\n" + "*FW build date:*\r\n" + formattedDate,
|
||||||
qrText};
|
qrText};
|
||||||
setEpdContent(epdContent); });
|
setEpdContent(epdContent); });
|
||||||
|
|
||||||
|
@ -179,8 +241,11 @@ void tryImprovSetup()
|
||||||
// esp_task_wdt_deinit();
|
// esp_task_wdt_deinit();
|
||||||
// esp_task_wdt_reset();
|
// esp_task_wdt_reset();
|
||||||
}
|
}
|
||||||
setFgColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR));
|
|
||||||
setBgColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR));
|
|
||||||
|
|
||||||
|
setFgColor(preferences.getUInt("fgColor", isWhiteVersion() ? GxEPD_BLACK : GxEPD_WHITE));
|
||||||
|
setBgColor(preferences.getUInt("bgColor", isWhiteVersion() ? GxEPD_WHITE : GxEPD_BLACK));
|
||||||
}
|
}
|
||||||
// else
|
// else
|
||||||
// {
|
// {
|
||||||
|
@ -193,19 +258,21 @@ void tryImprovSetup()
|
||||||
// queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS);
|
// queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupTime()
|
void syncTime()
|
||||||
{
|
{
|
||||||
configTime(preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS), 0,
|
configTime(preferences.getInt("gmtOffset", DEFAULT_TIME_OFFSET_SECONDS), 0,
|
||||||
NTP_SERVER);
|
NTP_SERVER);
|
||||||
struct tm timeinfo;
|
struct tm timeinfo;
|
||||||
|
|
||||||
while (!getLocalTime(&timeinfo))
|
while (!getLocalTime(&timeinfo))
|
||||||
{
|
{
|
||||||
configTime(preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS), 0,
|
configTime(preferences.getInt("gmtOffset", DEFAULT_TIME_OFFSET_SECONDS), 0,
|
||||||
NTP_SERVER);
|
NTP_SERVER);
|
||||||
delay(500);
|
delay(500);
|
||||||
Serial.println(F("Retry set time"));
|
Serial.println(F("Retry set time"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastTimeSync = esp_timer_get_time() / 1000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupPreferences()
|
void setupPreferences()
|
||||||
|
@ -214,28 +281,93 @@ void setupPreferences()
|
||||||
|
|
||||||
setFgColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR));
|
setFgColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR));
|
||||||
setBgColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR));
|
setBgColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR));
|
||||||
setBlockHeight(preferences.getUInt("blockHeight", 816000));
|
setBlockHeight(preferences.getUInt("blockHeight", INITIAL_BLOCK_HEIGHT));
|
||||||
setPrice(preferences.getUInt("lastPrice", 30000));
|
setPrice(preferences.getUInt("lastPrice", INITIAL_LAST_PRICE), CURRENCY_USD);
|
||||||
|
|
||||||
screenNameMap[SCREEN_BLOCK_HEIGHT] = "Block Height";
|
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE))
|
||||||
screenNameMap[SCREEN_BLOCK_FEE_RATE] = "Block Fee Rate";
|
setCurrentCurrency(preferences.getUChar("lastCurrency", CURRENCY_USD));
|
||||||
screenNameMap[SCREEN_MSCW_TIME] = "Sats per dollar";
|
else
|
||||||
screenNameMap[SCREEN_BTC_TICKER] = "Ticker";
|
setCurrentCurrency(CURRENCY_USD);
|
||||||
screenNameMap[SCREEN_TIME] = "Time";
|
|
||||||
screenNameMap[SCREEN_HALVING_COUNTDOWN] = "Halving countdown";
|
if (!preferences.isKey("flDisable")) {
|
||||||
screenNameMap[SCREEN_MARKET_CAP] = "Market Cap";
|
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");
|
||||||
|
|
||||||
|
addScreenMapping(SCREEN_SATS_PER_CURRENCY, "Sats per dollar");
|
||||||
|
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");
|
||||||
|
|
||||||
|
// addScreenMapping(SCREEN_SATS_PER_CURRENCY_EUR, "Sats per EUR");
|
||||||
|
// addScreenMapping(SCREEN_BTC_TICKER_EUR, "Ticker EUR");
|
||||||
|
// addScreenMapping(SCREEN_MARKET_CAP_EUR, "Market Cap EUR");
|
||||||
|
|
||||||
|
// screenNameMap[SCREEN_BLOCK_HEIGHT] = "Block Height";
|
||||||
|
// screenNameMap[SCREEN_BLOCK_FEE_RATE] = "Block Fee Rate";
|
||||||
|
// screenNameMap[SCREEN_SATS_PER_CURRENCY] = "Sats per dollar";
|
||||||
|
// screenNameMap[SCREEN_BTC_TICKER] = "Ticker";
|
||||||
|
// screenNameMap[SCREEN_TIME] = "Time";
|
||||||
|
// screenNameMap[SCREEN_HALVING_COUNTDOWN] = "Halving countdown";
|
||||||
|
// screenNameMap[SCREEN_MARKET_CAP] = "Market Cap";
|
||||||
|
|
||||||
|
// 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";
|
||||||
|
|
||||||
|
for (int i = 0; i < strlen(ambiguous); i++)
|
||||||
|
{
|
||||||
|
input.replace(ambiguous[i], replacements[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupWebsocketClients(void *pvParameters)
|
void setupWebsocketClients(void *pvParameters)
|
||||||
{
|
{
|
||||||
setupBlockNotify();
|
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE))
|
||||||
|
|
||||||
if (preferences.getBool("fetchEurPrice", false))
|
|
||||||
{
|
{
|
||||||
setupPriceFetchTask();
|
V2Notify::setupV2Notify();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
setupBlockNotify();
|
||||||
setupPriceNotify();
|
setupPriceNotify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +384,7 @@ void setupTimers()
|
||||||
|
|
||||||
void finishSetup()
|
void finishSetup()
|
||||||
{
|
{
|
||||||
if (preferences.getBool("ledStatus", false))
|
if (preferences.getBool("ledStatus", DEFAULT_LED_STATUS))
|
||||||
{
|
{
|
||||||
restoreLedState();
|
restoreLedState();
|
||||||
}
|
}
|
||||||
|
@ -262,16 +394,16 @@ void finishSetup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> getScreenNameMap() { return screenNameMap; }
|
std::vector<ScreenMapping> getScreenNameMap() { return screenMappings; }
|
||||||
|
|
||||||
void setupMcp()
|
void setupMcp()
|
||||||
{
|
{
|
||||||
#ifdef IS_BTCLOCK_S3
|
#ifdef IS_BTCLOCK_V8
|
||||||
const int mcp1AddrPins[] = {MCP1_A0_PIN, MCP1_A1_PIN, MCP1_A2_PIN};
|
const int mcp1AddrPins[] = {MCP1_A0_PIN, MCP1_A1_PIN, MCP1_A2_PIN};
|
||||||
const int mcp1AddrValues[] = {LOW, LOW, LOW};
|
const int mcp1AddrValues[] = {LOW, LOW, LOW};
|
||||||
|
|
||||||
const int mcp2AddrPins[] = {MCP2_A0_PIN, MCP2_A1_PIN, MCP2_A2_PIN};
|
const int mcp2AddrPins[] = {MCP2_A0_PIN, MCP2_A1_PIN, MCP2_A2_PIN};
|
||||||
const int mcp2AddrValues[] = {LOW, LOW, HIGH};
|
const int mcp2AddrValues[] = {HIGH, LOW, LOW};
|
||||||
|
|
||||||
pinMode(MCP_RESET_PIN, OUTPUT);
|
pinMode(MCP_RESET_PIN, OUTPUT);
|
||||||
digitalWrite(MCP_RESET_PIN, HIGH);
|
digitalWrite(MCP_RESET_PIN, HIGH);
|
||||||
|
@ -298,11 +430,30 @@ void setupHardware()
|
||||||
Serial.println(F("An Error has occurred while mounting LittleFS"));
|
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"))
|
if (!LittleFS.open("/index.html.gz", "r"))
|
||||||
{
|
{
|
||||||
Serial.println("Error loading WebUI");
|
Serial.println(F("Error loading WebUI"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// {
|
||||||
|
// 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();
|
setupLeds();
|
||||||
|
|
||||||
WiFi.setHostname(getMyHostname().c_str());
|
WiFi.setHostname(getMyHostname().c_str());
|
||||||
|
@ -315,248 +466,69 @@ void setupHardware()
|
||||||
|
|
||||||
Wire.begin(I2C_SDA_PIN, I2C_SCK_PIN, 400000);
|
Wire.begin(I2C_SDA_PIN, I2C_SCK_PIN, 400000);
|
||||||
|
|
||||||
if (!mcp1.begin_I2C(0x20))
|
if (!mcp1.begin()) {
|
||||||
{
|
Serial.println(F("Error MCP23017 1"));
|
||||||
Serial.println(F("Error MCP23017"));
|
} else {
|
||||||
|
|
||||||
// while (1)
|
|
||||||
// ;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pinMode(MCP_INT_PIN, INPUT_PULLUP);
|
pinMode(MCP_INT_PIN, INPUT_PULLUP);
|
||||||
mcp1.setupInterrupts(false, false, LOW);
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
// Enable mirrored interrupts (both INTA and INTB pins signal any interrupt)
|
||||||
{
|
if (!mcp1.mirrorInterrupts(true)) {
|
||||||
mcp1.pinMode(i, INPUT_PULLUP);
|
Serial.println(F("Error setting up mirrored interrupts"));
|
||||||
mcp1.setupInterruptPin(i, LOW);
|
|
||||||
}
|
}
|
||||||
#ifndef IS_BTCLOCK_S3
|
|
||||||
for (int i = 8; i <= 14; i++)
|
// Configure all 4 button pins as inputs with pullups and interrupts
|
||||||
{
|
for (int i = 0; i < 4; i++) {
|
||||||
mcp1.pinMode(i, OUTPUT);
|
if (!mcp1.pinMode1(i, INPUT_PULLUP)) {
|
||||||
|
Serial.printf("Error setting pin %d to input pull up\n", i);
|
||||||
}
|
}
|
||||||
|
// Enable interrupt on CHANGE for each pin
|
||||||
|
if (!mcp1.enableInterrupt(i, CHANGE)) {
|
||||||
|
Serial.printf("Error enabling interrupt for pin %d\n", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set interrupt pins as open drain with active-low polarity
|
||||||
|
if (!mcp1.setInterruptPolarity(2)) { // 2 = Open drain
|
||||||
|
Serial.println(F("Error setting interrupt polarity"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear any pending interrupts
|
||||||
|
mcp1.getInterruptCaptureRegister();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef IS_HW_REV_B
|
||||||
|
pinMode(39, INPUT_PULLDOWN);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef IS_BTCLOCK_S3
|
#ifdef IS_BTCLOCK_V8
|
||||||
if (!mcp2.begin_I2C(0x21))
|
if (!mcp2.begin())
|
||||||
{
|
{
|
||||||
Serial.println(F("Error MCP23017"));
|
Serial.println(F("Error MCP23017 2"));
|
||||||
|
|
||||||
// while (1)
|
// while (1)
|
||||||
// ;
|
// ;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
void improvGetAvailableWifiNetworks()
|
#ifdef HAS_FRONTLIGHT
|
||||||
{
|
setupFrontlight();
|
||||||
int networkNum = WiFi.scanNetworks();
|
|
||||||
|
|
||||||
for (int id = 0; id < networkNum; ++id)
|
Wire.beginTransmission(0x5C);
|
||||||
{
|
byte error = Wire.endTransmission();
|
||||||
std::vector<uint8_t> data = improv::build_rpc_response(
|
|
||||||
improv::GET_WIFI_NETWORKS,
|
|
||||||
{WiFi.SSID(id), String(WiFi.RSSI(id)),
|
|
||||||
(WiFi.encryptionType(id) == WIFI_AUTH_OPEN ? "NO" : "YES")},
|
|
||||||
false);
|
|
||||||
improv_send_response(data);
|
|
||||||
}
|
|
||||||
// final response
|
|
||||||
std::vector<uint8_t> data = improv::build_rpc_response(
|
|
||||||
improv::GET_WIFI_NETWORKS, std::vector<std::string>{}, false);
|
|
||||||
improv_send_response(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool improv_connectWifi(std::string ssid, std::string password)
|
if (error == 0)
|
||||||
{
|
{
|
||||||
uint8_t count = 0;
|
Serial.println(F("Found BH1750"));
|
||||||
|
hasLuxSensor = true;
|
||||||
WiFi.begin(ssid.c_str(), password.c_str());
|
bh1750.begin(BH1750::CONTINUOUS_HIGH_RES_MODE, 0x5C);
|
||||||
|
|
||||||
while (WiFi.status() != WL_CONNECTED)
|
|
||||||
{
|
|
||||||
blinkDelay(500, 2);
|
|
||||||
|
|
||||||
if (count > MAX_ATTEMPTS_WIFI_CONNECTION)
|
|
||||||
{
|
|
||||||
WiFi.disconnect();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void onImprovErrorCallback(improv::Error err)
|
|
||||||
{
|
|
||||||
blinkDelayColor(100, 1, 255, 0, 0);
|
|
||||||
// pixels.setPixelColor(0, pixels.Color(255, 0, 0));
|
|
||||||
// pixels.setPixelColor(1, pixels.Color(255, 0, 0));
|
|
||||||
// pixels.setPixelColor(2, pixels.Color(255, 0, 0));
|
|
||||||
// pixels.setPixelColor(3, pixels.Color(255, 0, 0));
|
|
||||||
// pixels.show();
|
|
||||||
// vTaskDelay(pdMS_TO_TICKS(100));
|
|
||||||
|
|
||||||
// pixels.clear();
|
|
||||||
// pixels.show();
|
|
||||||
// vTaskDelay(pdMS_TO_TICKS(100));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> getLocalUrl()
|
|
||||||
{
|
|
||||||
return {// URL where user can finish onboarding or use device
|
|
||||||
// Recommended to use website hosted by device
|
|
||||||
String("http://" + WiFi.localIP().toString()).c_str()};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool onImprovCommandCallback(improv::ImprovCommand cmd)
|
|
||||||
{
|
|
||||||
switch (cmd.command)
|
|
||||||
{
|
|
||||||
case improv::Command::GET_CURRENT_STATE:
|
|
||||||
{
|
|
||||||
if ((WiFi.status() == WL_CONNECTED))
|
|
||||||
{
|
|
||||||
improv_set_state(improv::State::STATE_PROVISIONED);
|
|
||||||
std::vector<uint8_t> data = improv::build_rpc_response(
|
|
||||||
improv::GET_CURRENT_STATE, getLocalUrl(), false);
|
|
||||||
improv_send_response(data);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
improv_set_state(improv::State::STATE_AUTHORIZED);
|
Serial.println(F("BH1750 Not found"));
|
||||||
|
hasLuxSensor = false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case improv::Command::WIFI_SETTINGS:
|
|
||||||
{
|
|
||||||
if (cmd.ssid.length() == 0)
|
|
||||||
{
|
|
||||||
improv_set_error(improv::Error::ERROR_INVALID_RPC);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
improv_set_state(improv::STATE_PROVISIONING);
|
|
||||||
queueLedEffect(LED_EFFECT_WIFI_CONNECTING);
|
|
||||||
|
|
||||||
if (improv_connectWifi(cmd.ssid, cmd.password))
|
|
||||||
{
|
|
||||||
queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS);
|
|
||||||
|
|
||||||
// std::array<String, NUM_SCREENS> epdContent = {"S", "U", "C", "C",
|
|
||||||
// "E", "S", "S"}; setEpdContent(epdContent);
|
|
||||||
|
|
||||||
preferences.putBool("wifiConfigured", true);
|
|
||||||
|
|
||||||
improv_set_state(improv::STATE_PROVISIONED);
|
|
||||||
std::vector<uint8_t> data = improv::build_rpc_response(
|
|
||||||
improv::WIFI_SETTINGS, getLocalUrl(), false);
|
|
||||||
improv_send_response(data);
|
|
||||||
|
|
||||||
delay(2500);
|
|
||||||
ESP.restart();
|
|
||||||
setupWebserver();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
|
|
||||||
|
|
||||||
improv_set_state(improv::STATE_STOPPED);
|
|
||||||
improv_set_error(improv::Error::ERROR_UNABLE_TO_CONNECT);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case improv::Command::GET_DEVICE_INFO:
|
|
||||||
{
|
|
||||||
std::vector<std::string> infos = {// Firmware name
|
|
||||||
"BTClock",
|
|
||||||
// Firmware version
|
|
||||||
"1.0.0",
|
|
||||||
// Hardware chip/variant
|
|
||||||
"ESP32S3",
|
|
||||||
// Device name
|
|
||||||
"BTClock"};
|
|
||||||
std::vector<uint8_t> data =
|
|
||||||
improv::build_rpc_response(improv::GET_DEVICE_INFO, infos, false);
|
|
||||||
improv_send_response(data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case improv::Command::GET_WIFI_NETWORKS:
|
|
||||||
{
|
|
||||||
improvGetAvailableWifiNetworks();
|
|
||||||
// std::array<String, NUM_SCREENS> epdContent = {"W", "E", "B", "W", "I",
|
|
||||||
// "F", "I"}; setEpdContent(epdContent);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
improv_set_error(improv::ERROR_UNKNOWN_RPC);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void improv_set_state(improv::State state)
|
|
||||||
{
|
|
||||||
std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'};
|
|
||||||
data.resize(11);
|
|
||||||
data[6] = improv::IMPROV_SERIAL_VERSION;
|
|
||||||
data[7] = improv::TYPE_CURRENT_STATE;
|
|
||||||
data[8] = 1;
|
|
||||||
data[9] = state;
|
|
||||||
|
|
||||||
uint8_t checksum = 0x00;
|
|
||||||
for (uint8_t d : data)
|
|
||||||
checksum += d;
|
|
||||||
data[10] = checksum;
|
|
||||||
|
|
||||||
Serial.write(data.data(), data.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void improv_send_response(std::vector<uint8_t> &response)
|
|
||||||
{
|
|
||||||
std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'};
|
|
||||||
data.resize(9);
|
|
||||||
data[6] = improv::IMPROV_SERIAL_VERSION;
|
|
||||||
data[7] = improv::TYPE_RPC_RESPONSE;
|
|
||||||
data[8] = response.size();
|
|
||||||
data.insert(data.end(), response.begin(), response.end());
|
|
||||||
|
|
||||||
uint8_t checksum = 0x00;
|
|
||||||
for (uint8_t d : data)
|
|
||||||
checksum += d;
|
|
||||||
data.push_back(checksum);
|
|
||||||
|
|
||||||
Serial.write(data.data(), data.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void improv_set_error(improv::Error error)
|
|
||||||
{
|
|
||||||
std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'};
|
|
||||||
data.resize(11);
|
|
||||||
data[6] = improv::IMPROV_SERIAL_VERSION;
|
|
||||||
data[7] = improv::TYPE_ERROR_STATE;
|
|
||||||
data[8] = 1;
|
|
||||||
data[9] = error;
|
|
||||||
|
|
||||||
uint8_t checksum = 0x00;
|
|
||||||
for (uint8_t d : data)
|
|
||||||
checksum += d;
|
|
||||||
data[10] = checksum;
|
|
||||||
|
|
||||||
Serial.write(data.data(), data.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
|
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
|
||||||
|
@ -568,25 +540,25 @@ void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
|
||||||
switch (event)
|
switch (event)
|
||||||
{
|
{
|
||||||
case ARDUINO_EVENT_WIFI_READY:
|
case ARDUINO_EVENT_WIFI_READY:
|
||||||
Serial.println("WiFi interface ready");
|
Serial.println(F("WiFi interface ready"));
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_SCAN_DONE:
|
case ARDUINO_EVENT_WIFI_SCAN_DONE:
|
||||||
Serial.println("Completed scan for access points");
|
Serial.println(F("Completed scan for access points"));
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_STA_START:
|
case ARDUINO_EVENT_WIFI_STA_START:
|
||||||
Serial.println("WiFi client started");
|
Serial.println(F("WiFi client started"));
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_STA_STOP:
|
case ARDUINO_EVENT_WIFI_STA_STOP:
|
||||||
Serial.println("WiFi clients stopped");
|
Serial.println(F("WiFi clients stopped"));
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
|
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
|
||||||
Serial.println("Connected to access point");
|
Serial.println(F("Connected to access point"));
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||||
{
|
{
|
||||||
if (!first_connect)
|
if (!first_connect)
|
||||||
{
|
{
|
||||||
Serial.println("Disconnected from WiFi access point");
|
Serial.println(F("Disconnected from WiFi access point"));
|
||||||
queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
|
queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
|
||||||
uint8_t reason = info.wifi_sta_disconnected.reason;
|
uint8_t reason = info.wifi_sta_disconnected.reason;
|
||||||
if (reason)
|
if (reason)
|
||||||
|
@ -596,7 +568,7 @@ void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE:
|
case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE:
|
||||||
Serial.println("Authentication mode of access point has changed");
|
Serial.println(F("Authentication mode of access point has changed"));
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||||
{
|
{
|
||||||
|
@ -608,33 +580,33 @@ void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ARDUINO_EVENT_WIFI_STA_LOST_IP:
|
case ARDUINO_EVENT_WIFI_STA_LOST_IP:
|
||||||
Serial.println("Lost IP address and IP address is reset to 0");
|
Serial.println(F("Lost IP address and IP address is reset to 0"));
|
||||||
queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
|
queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
|
||||||
WiFi.reconnect();
|
WiFi.reconnect();
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_AP_START:
|
case ARDUINO_EVENT_WIFI_AP_START:
|
||||||
Serial.println("WiFi access point started");
|
Serial.println(F("WiFi access point started"));
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_AP_STOP:
|
case ARDUINO_EVENT_WIFI_AP_STOP:
|
||||||
Serial.println("WiFi access point stopped");
|
Serial.println(F("WiFi access point stopped"));
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
|
case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
|
||||||
Serial.println("Client connected");
|
Serial.println(F("Client connected"));
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:
|
case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:
|
||||||
Serial.println("Client disconnected");
|
Serial.println(F("Client disconnected"));
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED:
|
case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED:
|
||||||
Serial.println("Assigned IP address to client");
|
Serial.println(F("Assigned IP address to client"));
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED:
|
case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED:
|
||||||
Serial.println("Received probe request");
|
Serial.println(F("Received probe request"));
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_AP_GOT_IP6:
|
case ARDUINO_EVENT_WIFI_AP_GOT_IP6:
|
||||||
Serial.println("AP IPv6 is preferred");
|
Serial.println(F("AP IPv6 is preferred"));
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
|
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
|
||||||
Serial.println("STA IPv6 is preferred");
|
Serial.println(F("STA IPv6 is preferred"));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -647,8 +619,151 @@ String getMyHostname()
|
||||||
// WiFi.macAddress(mac);
|
// WiFi.macAddress(mac);
|
||||||
esp_efuse_mac_get_default(mac);
|
esp_efuse_mac_get_default(mac);
|
||||||
char hostname[15];
|
char hostname[15];
|
||||||
String hostnamePrefix = preferences.getString("hostnamePrefix", "btclock");
|
String hostnamePrefix = preferences.getString("hostnamePrefix", DEFAULT_HOSTNAME_PREFIX);
|
||||||
snprintf(hostname, sizeof(hostname), "%s-%02x%02x%02x", hostnamePrefix,
|
snprintf(hostname, sizeof(hostname), "%s-%02x%02x%02x", hostnamePrefix,
|
||||||
mac[3], mac[4], mac[5]);
|
mac[3], mac[4], mac[5]);
|
||||||
return hostname;
|
return hostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint getLastTimeSync()
|
||||||
|
{
|
||||||
|
return lastTimeSync;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAS_FRONTLIGHT
|
||||||
|
void setupFrontlight()
|
||||||
|
{
|
||||||
|
if (!flArray.begin(PCA9685_MODE1_AUTOINCR | PCA9685_MODE1_ALLCALL, PCA9685_MODE2_TOTEMPOLE))
|
||||||
|
{
|
||||||
|
Serial.println(F("FL driver error"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Serial.println(F("FL driver active"));
|
||||||
|
|
||||||
|
if (!preferences.isKey("flMaxBrightness"))
|
||||||
|
{
|
||||||
|
preferences.putUInt("flMaxBrightness", DEFAULT_FL_MAX_BRIGHTNESS);
|
||||||
|
}
|
||||||
|
if (!preferences.isKey("flEffectDelay"))
|
||||||
|
{
|
||||||
|
preferences.putUInt("flEffectDelay", DEFAULT_FL_EFFECT_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preferences.isKey("flFlashOnUpd"))
|
||||||
|
{
|
||||||
|
preferences.putBool("flFlashOnUpd", DEFAULT_FL_FLASH_ON_UPDATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float getLightLevel()
|
||||||
|
{
|
||||||
|
return bh1750.readLightLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasLightLevel()
|
||||||
|
{
|
||||||
|
return hasLuxSensor;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
String getHwRev()
|
||||||
|
{
|
||||||
|
#ifndef HW_REV
|
||||||
|
return "REV_0";
|
||||||
|
#else
|
||||||
|
return HW_REV;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isWhiteVersion()
|
||||||
|
{
|
||||||
|
#ifdef IS_HW_REV_B
|
||||||
|
pinMode(39, INPUT_PULLDOWN);
|
||||||
|
return digitalRead(39);
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
String getFsRev()
|
||||||
|
{
|
||||||
|
File fsHash = LittleFS.open("/fs_hash.txt", "r");
|
||||||
|
if (!fsHash)
|
||||||
|
{
|
||||||
|
Serial.println(F("Error loading WebUI"));
|
||||||
|
}
|
||||||
|
|
||||||
|
String ret = fsHash.readString();
|
||||||
|
fsHash.close();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int findScreenIndexByValue(int value)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < screenMappings.size(); i++)
|
||||||
|
{
|
||||||
|
if (screenMappings[i].value == value)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1; // Return -1 if value is not found
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> getAvailableCurrencies()
|
||||||
|
{
|
||||||
|
return {CURRENCY_CODE_USD, CURRENCY_CODE_EUR, CURRENCY_CODE_GBP, CURRENCY_CODE_JPY, CURRENCY_CODE_AUD, CURRENCY_CODE_CAD};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> getActiveCurrencies()
|
||||||
|
{
|
||||||
|
std::vector<std::string> result;
|
||||||
|
|
||||||
|
// Convert Arduino String to std::string
|
||||||
|
std::string stdString = preferences.getString("actCurrencies", DEFAULT_ACTIVE_CURRENCIES).c_str();
|
||||||
|
|
||||||
|
// Use a stringstream to split the string
|
||||||
|
std::stringstream ss(stdString);
|
||||||
|
std::string item;
|
||||||
|
|
||||||
|
// Split the string by comma and add each part to the vector
|
||||||
|
while (std::getline(ss, item, ','))
|
||||||
|
{
|
||||||
|
result.push_back(item);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isActiveCurrency(std::string ¤cy)
|
||||||
|
{
|
||||||
|
std::vector<std::string> ac = getActiveCurrencies();
|
||||||
|
if (std::find(ac.begin(), ac.end(), currency) != ac.end())
|
||||||
|
{
|
||||||
|
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,56 +1,88 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Adafruit_MCP23X17.h>
|
#include <MCP23017.h>
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
#include <WiFiClientSecure.h>
|
#include <WiFiClientSecure.h>
|
||||||
#include <WiFiManager.h>
|
#include <WiFiManager.h>
|
||||||
#include <base64.h>
|
#include <base64.h>
|
||||||
#include <esp_task_wdt.h>
|
#include <esp_task_wdt.h>
|
||||||
|
#include <nvs_flash.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "lib/block_notify.hpp"
|
#include "lib/block_notify.hpp"
|
||||||
#include "lib/button_handler.hpp"
|
#include "lib/button_handler.hpp"
|
||||||
#include "lib/epd.hpp"
|
#include "lib/epd.hpp"
|
||||||
#include "lib/improv.hpp"
|
// #include "lib/improv.hpp"
|
||||||
#include "lib/led_handler.hpp"
|
#include "lib/led_handler.hpp"
|
||||||
#include "lib/ota.hpp"
|
#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"
|
||||||
|
|
||||||
#include "lib/price_notify.hpp"
|
#include "lib/price_notify.hpp"
|
||||||
#include "lib/screen_handler.hpp"
|
#include "lib/screen_handler.hpp"
|
||||||
#include "lib/shared.hpp"
|
#include "lib/shared.hpp"
|
||||||
#include "lib/webserver.hpp"
|
#include "lib/webserver.hpp"
|
||||||
|
#ifdef HAS_FRONTLIGHT
|
||||||
|
#include "PCA9685.h"
|
||||||
|
#include "BH1750.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define NTP_SERVER "pool.ntp.org"
|
#define NTP_SERVER "pool.ntp.org"
|
||||||
#define DEFAULT_MEMPOOL_INSTANCE "mempool.space"
|
#define DEFAULT_TIME_OFFSET_SECONDS 3600
|
||||||
#define TIME_OFFSET_SECONDS 3600
|
#ifndef MCP_DEV_ADDR
|
||||||
#define USER_AGENT "BTClock/2.0"
|
|
||||||
#define MCP_DEV_ADDR 0x20
|
#define MCP_DEV_ADDR 0x20
|
||||||
#define DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE 30
|
#endif
|
||||||
#define DEFAULT_MINUTES_FULL_REFRESH 60
|
|
||||||
|
|
||||||
#define DEFAULT_FG_COLOR GxEPD_WHITE
|
|
||||||
#define DEFAULT_BG_COLOR GxEPD_BLACK
|
|
||||||
|
|
||||||
void setup();
|
void setup();
|
||||||
void setupTime();
|
void syncTime();
|
||||||
|
uint getLastTimeSync();
|
||||||
void setupPreferences();
|
void setupPreferences();
|
||||||
void setupWebsocketClients(void *pvParameters);
|
void setupWebsocketClients(void *pvParameters);
|
||||||
void setupHardware();
|
void setupHardware();
|
||||||
void tryImprovSetup();
|
void setupWifi();
|
||||||
void setupTimers();
|
void setupTimers();
|
||||||
void finishSetup();
|
void finishSetup();
|
||||||
void setupMcp();
|
void setupMcp();
|
||||||
|
#ifdef HAS_FRONTLIGHT
|
||||||
|
void setupFrontlight();
|
||||||
|
float getLightLevel();
|
||||||
|
bool hasLightLevel();
|
||||||
|
extern PCA9685 flArray;
|
||||||
|
#endif
|
||||||
|
|
||||||
String getMyHostname();
|
String getMyHostname();
|
||||||
std::vector<std::string> getScreenNameMap();
|
std::vector<ScreenMapping> getScreenNameMap();
|
||||||
|
|
||||||
std::vector<std::string> getLocalUrl();
|
std::vector<std::string> getLocalUrl();
|
||||||
bool improv_connectWifi(std::string ssid, std::string password);
|
// bool improv_connectWifi(std::string ssid, std::string password);
|
||||||
void improvGetAvailableWifiNetworks();
|
// void improvGetAvailableWifiNetworks();
|
||||||
bool onImprovCommandCallback(improv::ImprovCommand cmd);
|
// bool onImprovCommandCallback(improv::ImprovCommand cmd);
|
||||||
void onImprovErrorCallback(improv::Error err);
|
// void onImprovErrorCallback(improv::Error err);
|
||||||
void improv_set_state(improv::State state);
|
// void improv_set_state(improv::State state);
|
||||||
void improv_send_response(std::vector<uint8_t> &response);
|
// void improv_send_response(std::vector<uint8_t> &response);
|
||||||
void improv_set_error(improv::Error error);
|
// void improv_set_error(improv::Error error);
|
||||||
|
//void addCurrencyMappings(const std::vector<std::string>& currencies);
|
||||||
|
std::vector<std::string> getActiveCurrencies();
|
||||||
|
std::vector<std::string> getAvailableCurrencies();
|
||||||
|
|
||||||
|
bool isActiveCurrency(std::string ¤cy);
|
||||||
|
|
||||||
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);
|
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||||
|
String getHwRev();
|
||||||
|
bool isWhiteVersion();
|
||||||
|
String getFsRev();
|
||||||
|
|
||||||
|
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();
|
79
src/lib/defaults.hpp
Normal file
79
src/lib/defaults.hpp
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
#define INITIAL_BLOCK_HEIGHT 851500
|
||||||
|
#define INITIAL_LAST_PRICE 50000
|
||||||
|
#define DEFAULT_TX_POWER 0
|
||||||
|
|
||||||
|
#define DEFAULT_MEMPOOL_SECURE true
|
||||||
|
#define DEFAULT_LED_TEST_ON_POWER true
|
||||||
|
#define DEFAULT_LED_FLASH_ON_UPD false
|
||||||
|
#define DEFAULT_LED_BRIGHTNESS 128
|
||||||
|
#define DEFAULT_STEAL_FOCUS false
|
||||||
|
#define DEFAULT_MCAP_BIG_CHAR true
|
||||||
|
#define DEFAULT_MDNS_ENABLED true
|
||||||
|
#define DEFAULT_OTA_ENABLED true
|
||||||
|
#define DEFAULT_FETCH_EUR_PRICE false
|
||||||
|
#define DEFAULT_USE_SATS_SYMBOL false
|
||||||
|
#define DEFAULT_USE_BLOCK_COUNTDOWN true
|
||||||
|
#define DEFAULT_SUFFIX_PRICE false
|
||||||
|
#define DEFAULT_DISABLE_LEDS false
|
||||||
|
#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
|
||||||
|
|
||||||
|
|
||||||
|
#define DEFAULT_TIME_OFFSET_SECONDS 3600
|
||||||
|
|
||||||
|
#define DEFAULT_HOSTNAME_PREFIX "btclock"
|
||||||
|
#define DEFAULT_MEMPOOL_INSTANCE "mempool.space"
|
||||||
|
|
||||||
|
#define DEFAULT_USE_NOSTR false
|
||||||
|
#define DEFAULT_NOSTR_NPUB "642317135fd4c4205323b9dea8af3270657e62d51dc31a657c0ec8aab31c6288"
|
||||||
|
#define DEFAULT_NOSTR_RELAY "wss://relay.primal.net"
|
||||||
|
|
||||||
|
#define DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE 30
|
||||||
|
#define DEFAULT_MINUTES_FULL_REFRESH 60
|
||||||
|
|
||||||
|
#define DEFAULT_FG_COLOR GxEPD_WHITE
|
||||||
|
#define DEFAULT_BG_COLOR GxEPD_BLACK
|
||||||
|
|
||||||
|
#define DEFAULT_WP_TIMEOUT 15*60
|
||||||
|
|
||||||
|
#define DEFAULT_FL_MAX_BRIGHTNESS 2048
|
||||||
|
#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
|
||||||
|
|
||||||
|
#define DEFAULT_LED_STATUS false
|
||||||
|
#define DEFAULT_TIMER_ACTIVE true
|
||||||
|
#define DEFAULT_TIMER_SECONDS 1800
|
||||||
|
#define DEFAULT_CURRENT_SCREEN 0
|
||||||
|
|
||||||
|
#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"
|
318
src/lib/epd.cpp
318
src/lib/epd.cpp
|
@ -1,6 +1,65 @@
|
||||||
#include "epd.hpp"
|
#include "epd.hpp"
|
||||||
|
|
||||||
#ifndef IS_BTCLOCK_S3
|
#ifdef IS_BTCLOCK_REV_B
|
||||||
|
Native_Pin EPD_CS[NUM_SCREENS] = {
|
||||||
|
Native_Pin(2),
|
||||||
|
Native_Pin(4),
|
||||||
|
Native_Pin(6),
|
||||||
|
Native_Pin(10),
|
||||||
|
Native_Pin(38),
|
||||||
|
Native_Pin(21),
|
||||||
|
Native_Pin(17),
|
||||||
|
};
|
||||||
|
Native_Pin EPD_BUSY[NUM_SCREENS] = {
|
||||||
|
Native_Pin(3),
|
||||||
|
Native_Pin(5),
|
||||||
|
Native_Pin(7),
|
||||||
|
Native_Pin(9),
|
||||||
|
Native_Pin(37),
|
||||||
|
Native_Pin(18),
|
||||||
|
Native_Pin(16),
|
||||||
|
};
|
||||||
|
MCP23X17_Pin EPD_RESET_MPD[NUM_SCREENS] = {
|
||||||
|
MCP23X17_Pin(mcp1, 8),
|
||||||
|
MCP23X17_Pin(mcp1, 9),
|
||||||
|
MCP23X17_Pin(mcp1, 10),
|
||||||
|
MCP23X17_Pin(mcp1, 11),
|
||||||
|
MCP23X17_Pin(mcp1, 12),
|
||||||
|
MCP23X17_Pin(mcp1, 13),
|
||||||
|
MCP23X17_Pin(mcp1, 14),
|
||||||
|
};
|
||||||
|
|
||||||
|
Native_Pin EPD_DC = Native_Pin(14);
|
||||||
|
#elif IS_BTCLOCK_V8
|
||||||
|
Native_Pin EPD_DC = Native_Pin(38);
|
||||||
|
|
||||||
|
MCP23X17_Pin EPD_BUSY[NUM_SCREENS] = {
|
||||||
|
MCP23X17_Pin(mcp1, 8),
|
||||||
|
MCP23X17_Pin(mcp1, 9),
|
||||||
|
MCP23X17_Pin(mcp1, 10),
|
||||||
|
MCP23X17_Pin(mcp1, 11),
|
||||||
|
MCP23X17_Pin(mcp1, 12),
|
||||||
|
MCP23X17_Pin(mcp1, 13),
|
||||||
|
MCP23X17_Pin(mcp1, 14),
|
||||||
|
MCP23X17_Pin(mcp1, 4),
|
||||||
|
};
|
||||||
|
|
||||||
|
MCP23X17_Pin EPD_CS[NUM_SCREENS] = {
|
||||||
|
MCP23X17_Pin(mcp2, 8), MCP23X17_Pin(mcp2, 10), MCP23X17_Pin(mcp2, 12),
|
||||||
|
MCP23X17_Pin(mcp2, 14), MCP23X17_Pin(mcp2, 0), MCP23X17_Pin(mcp2, 2),
|
||||||
|
MCP23X17_Pin(mcp2, 4), MCP23X17_Pin(mcp2, 6)};
|
||||||
|
|
||||||
|
MCP23X17_Pin EPD_RESET_MPD[NUM_SCREENS] = {
|
||||||
|
MCP23X17_Pin(mcp2, 9),
|
||||||
|
MCP23X17_Pin(mcp2, 11),
|
||||||
|
MCP23X17_Pin(mcp2, 13),
|
||||||
|
MCP23X17_Pin(mcp2, 15),
|
||||||
|
MCP23X17_Pin(mcp2, 1),
|
||||||
|
MCP23X17_Pin(mcp2, 3),
|
||||||
|
MCP23X17_Pin(mcp2, 5),
|
||||||
|
MCP23X17_Pin(mcp2, 7),
|
||||||
|
};
|
||||||
|
#else
|
||||||
Native_Pin EPD_CS[NUM_SCREENS] = {
|
Native_Pin EPD_CS[NUM_SCREENS] = {
|
||||||
Native_Pin(2),
|
Native_Pin(2),
|
||||||
Native_Pin(4),
|
Native_Pin(4),
|
||||||
|
@ -35,36 +94,6 @@ MCP23X17_Pin EPD_RESET_MPD[NUM_SCREENS] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
Native_Pin EPD_DC = Native_Pin(14);
|
Native_Pin EPD_DC = Native_Pin(14);
|
||||||
#else
|
|
||||||
Native_Pin EPD_DC = Native_Pin(38);
|
|
||||||
|
|
||||||
MCP23X17_Pin EPD_BUSY[NUM_SCREENS] = {
|
|
||||||
MCP23X17_Pin(mcp1, 8),
|
|
||||||
MCP23X17_Pin(mcp1, 9),
|
|
||||||
MCP23X17_Pin(mcp1, 10),
|
|
||||||
MCP23X17_Pin(mcp1, 11),
|
|
||||||
MCP23X17_Pin(mcp1, 12),
|
|
||||||
MCP23X17_Pin(mcp1, 13),
|
|
||||||
MCP23X17_Pin(mcp1, 14),
|
|
||||||
MCP23X17_Pin(mcp1, 4),
|
|
||||||
};
|
|
||||||
|
|
||||||
MCP23X17_Pin EPD_CS[NUM_SCREENS] = {
|
|
||||||
MCP23X17_Pin(mcp2, 8), MCP23X17_Pin(mcp2, 10), MCP23X17_Pin(mcp2, 12),
|
|
||||||
MCP23X17_Pin(mcp2, 14), MCP23X17_Pin(mcp2, 0), MCP23X17_Pin(mcp2, 2),
|
|
||||||
MCP23X17_Pin(mcp2, 4), MCP23X17_Pin(mcp2, 6)};
|
|
||||||
|
|
||||||
MCP23X17_Pin EPD_RESET_MPD[NUM_SCREENS] = {
|
|
||||||
MCP23X17_Pin(mcp2, 9),
|
|
||||||
MCP23X17_Pin(mcp2, 11),
|
|
||||||
MCP23X17_Pin(mcp2, 13),
|
|
||||||
MCP23X17_Pin(mcp2, 15),
|
|
||||||
MCP23X17_Pin(mcp2, 1),
|
|
||||||
MCP23X17_Pin(mcp2, 3),
|
|
||||||
MCP23X17_Pin(mcp2, 5),
|
|
||||||
MCP23X17_Pin(mcp2, 7),
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GxEPD2_BW<EPD_CLASS, EPD_CLASS::HEIGHT> displays[NUM_SCREENS] = {
|
GxEPD2_BW<EPD_CLASS, EPD_CLASS::HEIGHT> displays[NUM_SCREENS] = {
|
||||||
|
@ -75,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[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[5], &EPD_DC, &EPD_RESET_MPD[5], &EPD_BUSY[5]),
|
||||||
EPD_CLASS(&EPD_CS[6], &EPD_DC, &EPD_RESET_MPD[6], &EPD_BUSY[6]),
|
EPD_CLASS(&EPD_CS[6], &EPD_DC, &EPD_RESET_MPD[6], &EPD_BUSY[6]),
|
||||||
#ifdef IS_BTCLOCK_S3
|
#ifdef IS_BTCLOCK_V8
|
||||||
EPD_CLASS(&EPD_CS[7], &EPD_DC, &EPD_RESET_MPD[6], &EPD_BUSY[7]),
|
EPD_CLASS(&EPD_CS[7], &EPD_DC, &EPD_RESET_MPD[7], &EPD_BUSY[7]),
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -103,6 +132,15 @@ std::mutex epdMutex[NUM_SCREENS];
|
||||||
|
|
||||||
uint8_t qrcode[800];
|
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()
|
void forceFullRefresh()
|
||||||
{
|
{
|
||||||
for (uint i = 0; i < NUM_SCREENS; i++)
|
for (uint i = 0; i < NUM_SCREENS; i++)
|
||||||
|
@ -111,25 +149,6 @@ void forceFullRefresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void refreshFromMemory()
|
|
||||||
{
|
|
||||||
for (uint i = 0; i < NUM_SCREENS; i++)
|
|
||||||
{
|
|
||||||
int *taskParam = new int;
|
|
||||||
*taskParam = i;
|
|
||||||
|
|
||||||
xTaskCreate(
|
|
||||||
[](void *pvParameters)
|
|
||||||
{
|
|
||||||
const int epdIndex = *(int *)pvParameters;
|
|
||||||
delete (int *)pvParameters;
|
|
||||||
displays[epdIndex].refresh(false);
|
|
||||||
vTaskDelete(NULL);
|
|
||||||
},
|
|
||||||
"PrepareUpd", 4096, taskParam, tskIDLE_PRIORITY, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupDisplays()
|
void setupDisplays()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lockMcp(mcpMutex);
|
std::lock_guard<std::mutex> lockMcp(mcpMutex);
|
||||||
|
@ -141,7 +160,7 @@ void setupDisplays()
|
||||||
|
|
||||||
updateQueue = xQueueCreate(UPDATE_QUEUE_SIZE, sizeof(UpdateDisplayTaskItem));
|
updateQueue = xQueueCreate(UPDATE_QUEUE_SIZE, sizeof(UpdateDisplayTaskItem));
|
||||||
|
|
||||||
xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", 4096, NULL, 11, NULL);
|
xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", EPD_TASK_STACK_SIZE*2, NULL, 11, NULL);
|
||||||
|
|
||||||
for (uint i = 0; i < NUM_SCREENS; i++)
|
for (uint i = 0; i < NUM_SCREENS; i++)
|
||||||
{
|
{
|
||||||
|
@ -151,22 +170,25 @@ void setupDisplays()
|
||||||
int *taskParam = new int;
|
int *taskParam = new int;
|
||||||
*taskParam = i;
|
*taskParam = i;
|
||||||
|
|
||||||
xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), 2048, taskParam,
|
xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), EPD_TASK_STACK_SIZE, taskParam,
|
||||||
11, &tasks[i]); // create task
|
11, &tasks[i]); // create task
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hold lower button to enable "storage mode" (prevents burn-in of ePaper displays)
|
// Hold lower button to enable "storage mode" (prevents burn-in of ePaper displays)
|
||||||
if (mcp1.digitalRead(0) == LOW)
|
if (mcp1.read1(0) == LOW)
|
||||||
{
|
{
|
||||||
setFgColor(GxEPD_BLACK);
|
setFgColor(GxEPD_BLACK);
|
||||||
setBgColor(GxEPD_WHITE);
|
setBgColor(GxEPD_WHITE);
|
||||||
|
|
||||||
epdContent = {" ", " ", " ", " ", " ", " ", " "};
|
epdContent.fill("");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#ifdef IS_BTCLOCK_V8
|
||||||
|
epdContent = {"B", "T", "C", "L", "O", "C", "K", "v8"};
|
||||||
|
#else
|
||||||
epdContent = {"B", "T", "C", "L", "O", "C", "K"};
|
epdContent = {"B", "T", "C", "L", "O", "C", "K"};
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
setEpdContent(epdContent);
|
setEpdContent(epdContent);
|
||||||
|
@ -223,7 +245,7 @@ void prepareDisplayUpdateTask(void *pvParameters)
|
||||||
|
|
||||||
bool updatePartial = true;
|
bool updatePartial = true;
|
||||||
|
|
||||||
if (strstr(epdContent[epdIndex].c_str(), "/") != NULL)
|
if (epdContent[epdIndex].length() > 1 && strstr(epdContent[epdIndex].c_str(), "/") != NULL)
|
||||||
{
|
{
|
||||||
String top = epdContent[epdIndex].substring(
|
String top = epdContent[epdIndex].substring(
|
||||||
0, epdContent[epdIndex].indexOf("/"));
|
0, epdContent[epdIndex].indexOf("/"));
|
||||||
|
@ -235,13 +257,23 @@ void prepareDisplayUpdateTask(void *pvParameters)
|
||||||
{
|
{
|
||||||
renderQr(epdIndex, epdContent[epdIndex], updatePartial);
|
renderQr(epdIndex, epdContent[epdIndex], updatePartial);
|
||||||
}
|
}
|
||||||
|
else if (epdContent[epdIndex].startsWith(F("mdi")))
|
||||||
|
{
|
||||||
|
bool updated = renderIcon(epdIndex, epdContent[epdIndex], updatePartial);
|
||||||
|
if (!updated) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (epdContent[epdIndex].length() > 5)
|
else if (epdContent[epdIndex].length() > 5)
|
||||||
{
|
{
|
||||||
renderText(epdIndex, epdContent[epdIndex], updatePartial);
|
renderText(epdIndex, epdContent[epdIndex], updatePartial);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (epdContent[epdIndex].length() > 1 && epdContent[epdIndex].indexOf(".") == -1)
|
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].equals("STS"))
|
if (epdContent[epdIndex].equals("STS"))
|
||||||
{
|
{
|
||||||
|
@ -328,7 +360,11 @@ extern "C" void updateDisplay(void *pvParameters) noexcept
|
||||||
void splitText(const uint dispNum, const String &top, const String &bottom,
|
void splitText(const uint dispNum, const String &top, const String &bottom,
|
||||||
bool partial)
|
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].setFont(&FONT_SMALL);
|
||||||
displays[dispNum].setTextColor(getFgColor());
|
displays[dispNum].setTextColor(getFgColor());
|
||||||
|
|
||||||
|
@ -365,80 +401,83 @@ void splitText(const uint dispNum, const String &top, const String &bottom,
|
||||||
displays[dispNum].print(bottom);
|
displays[dispNum].print(bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showDigit(const uint dispNum, char chr, bool partial,
|
// Consolidate common display setup code into a helper function
|
||||||
const GFXfont *font)
|
void setupDisplay(const uint dispNum, const GFXfont *font) {
|
||||||
{
|
|
||||||
String str(chr);
|
|
||||||
|
|
||||||
if (chr == '.')
|
|
||||||
{
|
|
||||||
str = "!";
|
|
||||||
}
|
|
||||||
displays[dispNum].setRotation(2);
|
displays[dispNum].setRotation(2);
|
||||||
displays[dispNum].setFont(font);
|
displays[dispNum].setFont(font);
|
||||||
displays[dispNum].setTextColor(getFgColor());
|
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;
|
int16_t tbx, tby;
|
||||||
uint16_t tbw, tbh;
|
uint16_t tbw, tbh;
|
||||||
|
|
||||||
displays[dispNum].getTextBounds(str, 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 x = ((displays[dispNum].width() - tbw) / 2) - tbx;
|
||||||
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
|
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
|
||||||
|
|
||||||
// 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].setCursor(x, y);
|
||||||
displays[dispNum].print(str);
|
displays[dispNum].print(str);
|
||||||
|
|
||||||
if (chr == '.')
|
if (chr == '.') {
|
||||||
{
|
displays[dispNum].fillRect(x, y, displays[dispNum].width(),
|
||||||
displays[dispNum].fillRect(x, y, displays[dispNum].width(), round(displays[dispNum].height() * 0.9), getBgColor());
|
round(displays[dispNum].height() * 0.9), getBgColor());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// displays[dispNum].setCursor(10, 3);
|
int16_t calculateDescent(const GFXfont *font) {
|
||||||
// displays[dispNum].setFont(&FONT_SMALL);
|
int16_t maxDescent = 0;
|
||||||
// displays[dispNum].setTextColor(getFgColor());
|
for (uint16_t i = font->first; i <= font->last; i++) {
|
||||||
// displays[dispNum].println("Y = " + y);
|
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 showChars(const uint dispNum, const String &chars, bool partial,
|
||||||
const GFXfont *font)
|
const GFXfont *font)
|
||||||
{
|
{
|
||||||
displays[dispNum].setRotation(2);
|
setupDisplay(dispNum, font);
|
||||||
displays[dispNum].setFont(font);
|
|
||||||
displays[dispNum].setTextColor(getFgColor());
|
|
||||||
int16_t tbx, tby;
|
int16_t tbx, tby;
|
||||||
uint16_t tbw, tbh;
|
uint16_t tbw, tbh;
|
||||||
displays[dispNum].getTextBounds(chars, 0, 0, &tbx, &tby, &tbw, &tbh);
|
displays[dispNum].getTextBounds(chars, 0, 0, &tbx, &tby, &tbw, &tbh);
|
||||||
|
|
||||||
// center the bounding box by transposition of the origin:
|
// center the bounding box by transposition of the origin:
|
||||||
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
|
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
|
||||||
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
|
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
|
||||||
displays[dispNum].fillScreen(getBgColor());
|
|
||||||
|
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].setCursor(x, y);
|
||||||
displays[dispNum].print(chars);
|
displays[dispNum].print(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move x-position for the next character
|
||||||
|
x += font->glyph[c - font->first].xAdvance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int getBgColor() { return bgColor; }
|
int getBgColor() { return bgColor; }
|
||||||
|
@ -484,6 +523,58 @@ void renderText(const uint dispNum, const String &text, bool partial)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool 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());
|
||||||
|
|
||||||
|
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;
|
||||||
|
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());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void renderQr(const uint dispNum, const String &text, bool partial)
|
void renderQr(const uint dispNum, const String &text, bool partial)
|
||||||
{
|
{
|
||||||
#ifdef USE_QR
|
#ifdef USE_QR
|
||||||
|
@ -527,15 +618,12 @@ void waitUntilNoneBusy()
|
||||||
while (EPD_BUSY[i].digitalRead())
|
while (EPD_BUSY[i].digitalRead())
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
vTaskDelay(10);
|
vTaskDelay(BUSY_RETRY_DELAY);
|
||||||
if (count == 200)
|
|
||||||
{
|
if (count == BUSY_TIMEOUT_COUNT) {
|
||||||
// displays[i].init(0, false);
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
vTaskDelay(100);
|
} else if (count > BUSY_TIMEOUT_COUNT + 5) {
|
||||||
}
|
log_e("Display %d busy timeout", i);
|
||||||
else if (count > 205)
|
|
||||||
{
|
|
||||||
Serial.printf("Busy timeout %d", i);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <Fonts/FreeSansBold9pt7b.h>
|
#include <Fonts/FreeSansBold9pt7b.h>
|
||||||
#include <GxEPD2_BW.h>
|
#include <GxEPD2_BW.h>
|
||||||
|
|
||||||
|
|
||||||
#include <mcp23x17_pin.hpp>
|
#include <mcp23x17_pin.hpp>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <native_pin.hpp>
|
#include <native_pin.hpp>
|
||||||
|
@ -12,6 +13,8 @@
|
||||||
#include "fonts/fonts.hpp"
|
#include "fonts/fonts.hpp"
|
||||||
#include "lib/config.hpp"
|
#include "lib/config.hpp"
|
||||||
#include "lib/shared.hpp"
|
#include "lib/shared.hpp"
|
||||||
|
#include "icons/icons.h"
|
||||||
|
#include "mining_pool_stats_fetch.hpp"
|
||||||
|
|
||||||
#ifdef USE_QR
|
#ifdef USE_QR
|
||||||
#include "qrcodegen.h"
|
#include "qrcodegen.h"
|
||||||
|
@ -23,7 +26,6 @@ typedef struct {
|
||||||
} UpdateDisplayTaskItem;
|
} UpdateDisplayTaskItem;
|
||||||
|
|
||||||
void forceFullRefresh();
|
void forceFullRefresh();
|
||||||
void refreshFromMemory();
|
|
||||||
void setupDisplays();
|
void setupDisplays();
|
||||||
|
|
||||||
void splitText(const uint dispNum, const String &top, const String &bottom,
|
void splitText(const uint dispNum, const String &top, const String &bottom,
|
||||||
|
@ -42,6 +44,7 @@ int getFgColor();
|
||||||
void setBgColor(int color);
|
void setBgColor(int color);
|
||||||
void setFgColor(int color);
|
void setFgColor(int color);
|
||||||
|
|
||||||
|
bool renderIcon(const uint dispNum, const String &text, bool partial);
|
||||||
void renderText(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);
|
void renderQr(const uint dispNum, const String &text, bool partial);
|
||||||
|
|
||||||
|
|
|
@ -1,143 +0,0 @@
|
||||||
#include "improv.h"
|
|
||||||
|
|
||||||
namespace improv {
|
|
||||||
|
|
||||||
ImprovCommand parse_improv_data(const std::vector<uint8_t> &data,
|
|
||||||
bool check_checksum) {
|
|
||||||
return parse_improv_data(data.data(), data.size(), check_checksum);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImprovCommand parse_improv_data(const uint8_t *data, size_t length,
|
|
||||||
bool check_checksum) {
|
|
||||||
ImprovCommand improv_command;
|
|
||||||
Command command = (Command)data[0];
|
|
||||||
uint8_t data_length = data[1];
|
|
||||||
|
|
||||||
if (data_length != length - 2 - check_checksum) {
|
|
||||||
improv_command.command = UNKNOWN;
|
|
||||||
return improv_command;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (check_checksum) {
|
|
||||||
uint8_t checksum = data[length - 1];
|
|
||||||
|
|
||||||
uint32_t calculated_checksum = 0;
|
|
||||||
for (uint8_t i = 0; i < length - 1; i++) {
|
|
||||||
calculated_checksum += data[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((uint8_t)calculated_checksum != checksum) {
|
|
||||||
improv_command.command = BAD_CHECKSUM;
|
|
||||||
return improv_command;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command == WIFI_SETTINGS) {
|
|
||||||
uint8_t ssid_length = data[2];
|
|
||||||
uint8_t ssid_start = 3;
|
|
||||||
size_t ssid_end = ssid_start + ssid_length;
|
|
||||||
|
|
||||||
uint8_t pass_length = data[ssid_end];
|
|
||||||
size_t pass_start = ssid_end + 1;
|
|
||||||
size_t pass_end = pass_start + pass_length;
|
|
||||||
|
|
||||||
std::string ssid(data + ssid_start, data + ssid_end);
|
|
||||||
std::string password(data + pass_start, data + pass_end);
|
|
||||||
return {.command = command, .ssid = ssid, .password = password};
|
|
||||||
}
|
|
||||||
|
|
||||||
improv_command.command = command;
|
|
||||||
return improv_command;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parse_improv_serial_byte(size_t position, uint8_t byte,
|
|
||||||
const uint8_t *buffer,
|
|
||||||
std::function<bool(ImprovCommand)> &&callback,
|
|
||||||
std::function<void(Error)> &&on_error) {
|
|
||||||
if (position == 0) return byte == 'I';
|
|
||||||
if (position == 1) return byte == 'M';
|
|
||||||
if (position == 2) return byte == 'P';
|
|
||||||
if (position == 3) return byte == 'R';
|
|
||||||
if (position == 4) return byte == 'O';
|
|
||||||
if (position == 5) return byte == 'V';
|
|
||||||
|
|
||||||
if (position == 6) return byte == IMPROV_SERIAL_VERSION;
|
|
||||||
|
|
||||||
if (position <= 8) return true;
|
|
||||||
|
|
||||||
uint8_t type = buffer[7];
|
|
||||||
uint8_t data_len = buffer[8];
|
|
||||||
|
|
||||||
if (position <= 8 + data_len) return true;
|
|
||||||
|
|
||||||
if (position == 8 + data_len + 1) {
|
|
||||||
uint8_t checksum = 0x00;
|
|
||||||
for (size_t i = 0; i < position; i++) checksum += buffer[i];
|
|
||||||
|
|
||||||
if (checksum != byte) {
|
|
||||||
on_error(ERROR_INVALID_RPC);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == TYPE_RPC) {
|
|
||||||
auto command = parse_improv_data(&buffer[9], data_len, false);
|
|
||||||
return callback(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> build_rpc_response(Command command,
|
|
||||||
const std::vector<std::string> &datum,
|
|
||||||
bool add_checksum) {
|
|
||||||
std::vector<uint8_t> out;
|
|
||||||
uint32_t length = 0;
|
|
||||||
out.push_back(command);
|
|
||||||
for (const auto &str : datum) {
|
|
||||||
uint8_t len = str.length();
|
|
||||||
length += len + 1;
|
|
||||||
out.push_back(len);
|
|
||||||
out.insert(out.end(), str.begin(), str.end());
|
|
||||||
}
|
|
||||||
out.insert(out.begin() + 1, length);
|
|
||||||
|
|
||||||
if (add_checksum) {
|
|
||||||
uint32_t calculated_checksum = 0;
|
|
||||||
|
|
||||||
for (uint8_t byte : out) {
|
|
||||||
calculated_checksum += byte;
|
|
||||||
}
|
|
||||||
out.push_back(calculated_checksum);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ARDUINO
|
|
||||||
std::vector<uint8_t> build_rpc_response(Command command,
|
|
||||||
const std::vector<String> &datum,
|
|
||||||
bool add_checksum) {
|
|
||||||
std::vector<uint8_t> out;
|
|
||||||
uint32_t length = 0;
|
|
||||||
out.push_back(command);
|
|
||||||
for (const auto &str : datum) {
|
|
||||||
uint8_t len = str.length();
|
|
||||||
length += len;
|
|
||||||
out.push_back(len);
|
|
||||||
out.insert(out.end(), str.begin(), str.end());
|
|
||||||
}
|
|
||||||
out.insert(out.begin() + 1, length);
|
|
||||||
|
|
||||||
if (add_checksum) {
|
|
||||||
uint32_t calculated_checksum = 0;
|
|
||||||
|
|
||||||
for (uint8_t byte : out) {
|
|
||||||
calculated_checksum += byte;
|
|
||||||
}
|
|
||||||
out.push_back(calculated_checksum);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
#endif // ARDUINO
|
|
||||||
|
|
||||||
} // namespace improv
|
|
|
@ -1,86 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef ARDUINO
|
|
||||||
#include <Arduino.h>
|
|
||||||
#endif // ARDUINO
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace improv {
|
|
||||||
|
|
||||||
static const char *const SERVICE_UUID = "00467768-6228-2272-4663-277478268000";
|
|
||||||
static const char *const STATUS_UUID = "00467768-6228-2272-4663-277478268001";
|
|
||||||
static const char *const ERROR_UUID = "00467768-6228-2272-4663-277478268002";
|
|
||||||
static const char *const RPC_COMMAND_UUID =
|
|
||||||
"00467768-6228-2272-4663-277478268003";
|
|
||||||
static const char *const RPC_RESULT_UUID =
|
|
||||||
"00467768-6228-2272-4663-277478268004";
|
|
||||||
static const char *const CAPABILITIES_UUID =
|
|
||||||
"00467768-6228-2272-4663-277478268005";
|
|
||||||
|
|
||||||
enum Error : uint8_t {
|
|
||||||
ERROR_NONE = 0x00,
|
|
||||||
ERROR_INVALID_RPC = 0x01,
|
|
||||||
ERROR_UNKNOWN_RPC = 0x02,
|
|
||||||
ERROR_UNABLE_TO_CONNECT = 0x03,
|
|
||||||
ERROR_NOT_AUTHORIZED = 0x04,
|
|
||||||
ERROR_UNKNOWN = 0xFF,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum State : uint8_t {
|
|
||||||
STATE_STOPPED = 0x00,
|
|
||||||
STATE_AWAITING_AUTHORIZATION = 0x01,
|
|
||||||
STATE_AUTHORIZED = 0x02,
|
|
||||||
STATE_PROVISIONING = 0x03,
|
|
||||||
STATE_PROVISIONED = 0x04,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Command : uint8_t {
|
|
||||||
UNKNOWN = 0x00,
|
|
||||||
WIFI_SETTINGS = 0x01,
|
|
||||||
IDENTIFY = 0x02,
|
|
||||||
GET_CURRENT_STATE = 0x02,
|
|
||||||
GET_DEVICE_INFO = 0x03,
|
|
||||||
GET_WIFI_NETWORKS = 0x04,
|
|
||||||
BAD_CHECKSUM = 0xFF,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint8_t CAPABILITY_IDENTIFY = 0x01;
|
|
||||||
static const uint8_t IMPROV_SERIAL_VERSION = 1;
|
|
||||||
|
|
||||||
enum ImprovSerialType : uint8_t {
|
|
||||||
TYPE_CURRENT_STATE = 0x01,
|
|
||||||
TYPE_ERROR_STATE = 0x02,
|
|
||||||
TYPE_RPC = 0x03,
|
|
||||||
TYPE_RPC_RESPONSE = 0x04
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ImprovCommand {
|
|
||||||
Command command;
|
|
||||||
std::string ssid;
|
|
||||||
std::string password;
|
|
||||||
};
|
|
||||||
|
|
||||||
ImprovCommand parse_improv_data(const std::vector<uint8_t> &data,
|
|
||||||
bool check_checksum = true);
|
|
||||||
ImprovCommand parse_improv_data(const uint8_t *data, size_t length,
|
|
||||||
bool check_checksum = true);
|
|
||||||
|
|
||||||
bool parse_improv_serial_byte(size_t position, uint8_t byte,
|
|
||||||
const uint8_t *buffer,
|
|
||||||
std::function<bool(ImprovCommand)> &&callback,
|
|
||||||
std::function<void(Error)> &&on_error);
|
|
||||||
|
|
||||||
std::vector<uint8_t> build_rpc_response(Command command,
|
|
||||||
const std::vector<std::string> &datum,
|
|
||||||
bool add_checksum = true);
|
|
||||||
#ifdef ARDUINO
|
|
||||||
std::vector<uint8_t> build_rpc_response(Command command,
|
|
||||||
const std::vector<String> &datum,
|
|
||||||
bool add_checksum = true);
|
|
||||||
#endif // ARDUINO
|
|
||||||
|
|
||||||
} // namespace improv
|
|
|
@ -5,25 +5,242 @@ QueueHandle_t ledTaskQueue = NULL;
|
||||||
Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
|
Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
|
||||||
uint ledTaskParams;
|
uint ledTaskParams;
|
||||||
|
|
||||||
void ledTask(void *parameter) {
|
#ifdef HAS_FRONTLIGHT
|
||||||
while (1) {
|
#define FL_FADE_STEP 25
|
||||||
if (ledTaskQueue != NULL) {
|
|
||||||
if (xQueueReceive(ledTaskQueue, &ledTaskParams, portMAX_DELAY) ==
|
|
||||||
pdPASS) {
|
|
||||||
|
|
||||||
if (preferences.getBool("disableLeds", false)) {
|
bool frontlightOn = false;
|
||||||
|
bool flInTransition = false;
|
||||||
|
|
||||||
|
void frontlightFlash(int flDelayTime)
|
||||||
|
{
|
||||||
|
if (preferences.getBool("flDisable"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (frontlightOn)
|
||||||
|
{
|
||||||
|
frontlightFadeOutAll(flDelayTime, true);
|
||||||
|
frontlightFadeInAll(flDelayTime, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
frontlightFadeInAll(flDelayTime, true);
|
||||||
|
frontlightFadeOutAll(flDelayTime, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void frontlightFadeInAll()
|
||||||
|
{
|
||||||
|
frontlightFadeInAll(preferences.getUInt("flEffectDelay"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void frontlightFadeOutAll()
|
||||||
|
{
|
||||||
|
frontlightFadeOutAll(preferences.getUInt("flEffectDelay"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void frontlightFadeIn(uint num)
|
||||||
|
{
|
||||||
|
frontlightFadeIn(num, preferences.getUInt("flEffectDelay"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void frontlightFadeOut(uint num)
|
||||||
|
{
|
||||||
|
frontlightFadeOut(num, preferences.getUInt("flEffectDelay"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void frontlightSetBrightness(uint brightness)
|
||||||
|
{
|
||||||
|
if (brightness > 4096)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int ledPin = 0; ledPin <= NUM_SCREENS; ledPin++)
|
||||||
|
{
|
||||||
|
flArray.setPWM(ledPin, 0, brightness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void frontlightFadeInAll(int flDelayTime)
|
||||||
|
{
|
||||||
|
frontlightFadeInAll(flDelayTime, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void frontlightFadeInAll(int flDelayTime, bool staggered)
|
||||||
|
{
|
||||||
|
if (preferences.getBool("flDisable"))
|
||||||
|
return;
|
||||||
|
if (frontlightIsOn())
|
||||||
|
return;
|
||||||
|
if (flInTransition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
flInTransition = true;
|
||||||
|
|
||||||
|
if (staggered)
|
||||||
|
{
|
||||||
|
int maxBrightness = preferences.getUInt("flMaxBrightness");
|
||||||
|
int step = FL_FADE_STEP;
|
||||||
|
int staggerDelay = flDelayTime / NUM_SCREENS;
|
||||||
|
|
||||||
|
for (int dutyCycle = 0; dutyCycle <= maxBrightness + (NUM_SCREENS - 1) * maxBrightness / NUM_SCREENS; dutyCycle += step)
|
||||||
|
{
|
||||||
|
for (int ledPin = 0; ledPin < NUM_SCREENS; ledPin++)
|
||||||
|
{
|
||||||
|
int ledBrightness = dutyCycle - ledPin * maxBrightness / NUM_SCREENS;
|
||||||
|
if (ledBrightness < 0)
|
||||||
|
ledBrightness = 0;
|
||||||
|
else if (ledBrightness > maxBrightness)
|
||||||
|
ledBrightness = maxBrightness;
|
||||||
|
|
||||||
|
flArray.setPWM(ledPin + 1, 0, ledBrightness);
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(staggerDelay));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int dutyCycle = 0; dutyCycle <= preferences.getUInt("flMaxBrightness"); dutyCycle += FL_FADE_STEP)
|
||||||
|
{
|
||||||
|
for (int ledPin = 0; ledPin <= NUM_SCREENS; ledPin++)
|
||||||
|
{
|
||||||
|
flArray.setPWM(ledPin, 0, dutyCycle);
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(flDelayTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frontlightOn = true;
|
||||||
|
flInTransition = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void frontlightFadeOutAll(int flDelayTime)
|
||||||
|
{
|
||||||
|
frontlightFadeOutAll(flDelayTime, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void frontlightFadeOutAll(int flDelayTime, bool staggered)
|
||||||
|
{
|
||||||
|
if (preferences.getBool("flDisable"))
|
||||||
|
return;
|
||||||
|
if (!frontlightIsOn())
|
||||||
|
return;
|
||||||
|
if (flInTransition)
|
||||||
|
return;
|
||||||
|
flInTransition = true;
|
||||||
|
|
||||||
|
if (staggered)
|
||||||
|
{
|
||||||
|
int maxBrightness = preferences.getUInt("flMaxBrightness");
|
||||||
|
int step = FL_FADE_STEP;
|
||||||
|
int staggerDelay = flDelayTime / NUM_SCREENS;
|
||||||
|
|
||||||
|
for (int dutyCycle = maxBrightness; dutyCycle >= 0; dutyCycle -= step)
|
||||||
|
{
|
||||||
|
for (int ledPin = 0; ledPin < NUM_SCREENS; ledPin++)
|
||||||
|
{
|
||||||
|
int ledBrightness = dutyCycle - (NUM_SCREENS - 1 - ledPin) * maxBrightness / NUM_SCREENS;
|
||||||
|
if (ledBrightness < 0)
|
||||||
|
ledBrightness = 0;
|
||||||
|
else if (ledBrightness > maxBrightness)
|
||||||
|
ledBrightness = maxBrightness;
|
||||||
|
|
||||||
|
flArray.setPWM(ledPin + 1, 0, ledBrightness);
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(staggerDelay));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int dutyCycle = preferences.getUInt("flMaxBrightness"); dutyCycle >= 0; dutyCycle -= FL_FADE_STEP)
|
||||||
|
{
|
||||||
|
for (int ledPin = 0; ledPin <= NUM_SCREENS; ledPin++)
|
||||||
|
{
|
||||||
|
flArray.setPWM(ledPin, 0, dutyCycle);
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(flDelayTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flArray.allOFF();
|
||||||
|
frontlightOn = false;
|
||||||
|
flInTransition = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint16_t> frontlightGetStatus()
|
||||||
|
{
|
||||||
|
std::vector<uint16_t> statuses;
|
||||||
|
for (int ledPin = 1; ledPin <= NUM_SCREENS; ledPin++)
|
||||||
|
{
|
||||||
|
uint16_t a = 0, b = 0;
|
||||||
|
flArray.getPWM(ledPin, &a, &b);
|
||||||
|
statuses.push_back(round(b - a / 4096));
|
||||||
|
}
|
||||||
|
|
||||||
|
return statuses;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool frontlightIsOn()
|
||||||
|
{
|
||||||
|
return frontlightOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void frontlightFadeIn(uint num, int flDelayTime)
|
||||||
|
{
|
||||||
|
if (preferences.getBool("flDisable"))
|
||||||
|
return;
|
||||||
|
for (int dutyCycle = 0; dutyCycle <= preferences.getUInt("flMaxBrightness"); dutyCycle += 5)
|
||||||
|
{
|
||||||
|
flArray.setPWM(num, 0, dutyCycle);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(flDelayTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void frontlightFadeOut(uint num, int flDelayTime)
|
||||||
|
{
|
||||||
|
if (preferences.getBool("flDisable"))
|
||||||
|
return;
|
||||||
|
if (!frontlightIsOn())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int dutyCycle = preferences.getUInt("flMaxBrightness"); dutyCycle >= 0; dutyCycle -= 5)
|
||||||
|
{
|
||||||
|
flArray.setPWM(num, 0, dutyCycle);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(flDelayTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void ledTask(void *parameter)
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (ledTaskQueue != NULL)
|
||||||
|
{
|
||||||
|
if (xQueueReceive(ledTaskQueue, &ledTaskParams, portMAX_DELAY) ==
|
||||||
|
pdPASS)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (preferences.getBool("disableLeds", DEFAULT_DISABLE_LEDS))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t oldLights[NEOPIXEL_COUNT];
|
uint32_t oldLights[NEOPIXEL_COUNT];
|
||||||
|
|
||||||
// get current state
|
// get current state
|
||||||
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
|
for (int i = 0; i < NEOPIXEL_COUNT; i++)
|
||||||
|
{
|
||||||
oldLights[i] = pixels.getPixelColor(i);
|
oldLights[i] = pixels.getPixelColor(i);
|
||||||
}
|
}
|
||||||
|
#ifdef HAS_FRONTLIGHT
|
||||||
switch (ledTaskParams) {
|
uint flDelayTime = preferences.getUInt("flEffectDelay");
|
||||||
|
#endif
|
||||||
|
switch (ledTaskParams)
|
||||||
|
{
|
||||||
case LED_POWER_TEST:
|
case LED_POWER_TEST:
|
||||||
|
#ifdef HAS_FRONTLIGHT
|
||||||
|
frontlightFadeInAll(preferences.getUInt("flEffectDelay"), true);
|
||||||
|
#endif
|
||||||
ledRainbow(20);
|
ledRainbow(20);
|
||||||
pixels.clear();
|
pixels.clear();
|
||||||
break;
|
break;
|
||||||
|
@ -43,6 +260,12 @@ void ledTask(void *parameter) {
|
||||||
case LED_DATA_PRICE_ERROR:
|
case LED_DATA_PRICE_ERROR:
|
||||||
blinkDelayColor(150, 2, 177, 90, 31);
|
blinkDelayColor(150, 2, 177, 90, 31);
|
||||||
break;
|
break;
|
||||||
|
case LED_FLASH_IDENTIFY:
|
||||||
|
blinkDelayTwoColor(100, 2, pixels.Color(255, 0, 0),
|
||||||
|
pixels.Color(0, 255, 255));
|
||||||
|
blinkDelayTwoColor(100, 2, pixels.Color(0, 255, 0),
|
||||||
|
pixels.Color(0, 0, 255));
|
||||||
|
break;
|
||||||
case LED_EFFECT_WIFI_CONNECT_SUCCESS:
|
case LED_EFFECT_WIFI_CONNECT_SUCCESS:
|
||||||
case LED_FLASH_SUCCESS:
|
case LED_FLASH_SUCCESS:
|
||||||
blinkDelayColor(150, 3, 0, 255, 0);
|
blinkDelayColor(150, 3, 0, 255, 0);
|
||||||
|
@ -57,12 +280,88 @@ void ledTask(void *parameter) {
|
||||||
pixels.setPixelColor(3, pixels.Color(0, 255, 0));
|
pixels.setPixelColor(3, pixels.Color(0, 255, 0));
|
||||||
pixels.show();
|
pixels.show();
|
||||||
break;
|
break;
|
||||||
|
case LED_EFFECT_NOSTR_ZAP:
|
||||||
|
{
|
||||||
|
#ifdef HAS_FRONTLIGHT
|
||||||
|
bool frontlightWasOn = false;
|
||||||
|
|
||||||
|
if (preferences.getBool("flFlashOnZap", DEFAULT_FL_FLASH_ON_ZAP))
|
||||||
|
{
|
||||||
|
if (frontlightOn)
|
||||||
|
{
|
||||||
|
frontlightWasOn = true;
|
||||||
|
frontlightFadeOutAll(flDelayTime, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
frontlightFadeInAll(flDelayTime, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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));
|
||||||
|
#ifdef HAS_FRONTLIGHT
|
||||||
|
if (preferences.getBool("flFlashOnZap", DEFAULT_FL_FLASH_ON_ZAP))
|
||||||
|
{
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
if (frontlightWasOn)
|
||||||
|
{
|
||||||
|
frontlightFadeInAll(flDelayTime, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
frontlightFadeOutAll(flDelayTime, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
case LED_FLASH_UPDATE:
|
case LED_FLASH_UPDATE:
|
||||||
|
blinkDelayTwoColor(250, 3, pixels.Color(0, 230, 0),
|
||||||
|
pixels.Color(230, 230, 0));
|
||||||
break;
|
break;
|
||||||
case LED_FLASH_BLOCK_NOTIFY:
|
case LED_FLASH_BLOCK_NOTIFY:
|
||||||
|
{
|
||||||
|
#ifdef HAS_FRONTLIGHT
|
||||||
|
bool frontlightWasOn = false;
|
||||||
|
|
||||||
|
if (preferences.getBool("flFlashOnUpd", DEFAULT_FL_FLASH_ON_UPDATE))
|
||||||
|
{
|
||||||
|
if (frontlightOn)
|
||||||
|
{
|
||||||
|
frontlightWasOn = true;
|
||||||
|
frontlightFadeOutAll(flDelayTime, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
frontlightFadeInAll(flDelayTime, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
blinkDelayTwoColor(250, 3, pixels.Color(224, 67, 0),
|
blinkDelayTwoColor(250, 3, pixels.Color(224, 67, 0),
|
||||||
pixels.Color(8, 2, 0));
|
pixels.Color(8, 2, 0));
|
||||||
|
#ifdef HAS_FRONTLIGHT
|
||||||
|
if (preferences.getBool("flFlashOnUpd", DEFAULT_FL_FLASH_ON_UPDATE))
|
||||||
|
{
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
if (frontlightWasOn)
|
||||||
|
{
|
||||||
|
frontlightFadeInAll(flDelayTime, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
frontlightFadeOutAll(flDelayTime, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case LED_EFFECT_WIFI_WAIT_FOR_CONFIG:
|
case LED_EFFECT_WIFI_WAIT_FOR_CONFIG:
|
||||||
blinkDelayTwoColor(100, 1, pixels.Color(8, 161, 236),
|
blinkDelayTwoColor(100, 1, pixels.Color(8, 161, 236),
|
||||||
pixels.Color(156, 225, 240));
|
pixels.Color(156, 225, 240));
|
||||||
|
@ -71,11 +370,16 @@ void ledTask(void *parameter) {
|
||||||
blinkDelay(100, 3);
|
blinkDelay(100, 3);
|
||||||
break;
|
break;
|
||||||
case LED_EFFECT_WIFI_CONNECTING:
|
case LED_EFFECT_WIFI_CONNECTING:
|
||||||
for (int i = NEOPIXEL_COUNT; i >= 0; i--) {
|
for (int i = NEOPIXEL_COUNT; i >= 0; i--)
|
||||||
for (int j = NEOPIXEL_COUNT; j >= 0; j--) {
|
{
|
||||||
if (j == i) {
|
for (int j = NEOPIXEL_COUNT; j >= 0; j--)
|
||||||
|
{
|
||||||
|
if (j == i)
|
||||||
|
{
|
||||||
pixels.setPixelColor(i, pixels.Color(16, 197, 236));
|
pixels.setPixelColor(i, pixels.Color(16, 197, 236));
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
pixels.setPixelColor(j, pixels.Color(0, 0, 0));
|
pixels.setPixelColor(j, pixels.Color(0, 0, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,10 +388,13 @@ void ledTask(void *parameter) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LED_EFFECT_PAUSE_TIMER:
|
case LED_EFFECT_PAUSE_TIMER:
|
||||||
for (int i = NEOPIXEL_COUNT; i >= 0; i--) {
|
for (int i = NEOPIXEL_COUNT; i >= 0; i--)
|
||||||
for (int j = NEOPIXEL_COUNT; j >= 0; j--) {
|
{
|
||||||
|
for (int j = NEOPIXEL_COUNT; j >= 0; j--)
|
||||||
|
{
|
||||||
uint32_t c = pixels.Color(0, 0, 0);
|
uint32_t c = pixels.Color(0, 0, 0);
|
||||||
if (i == j) c = pixels.Color(0, 255, 0);
|
if (i == j)
|
||||||
|
c = pixels.Color(0, 255, 0);
|
||||||
pixels.setPixelColor(j, c);
|
pixels.setPixelColor(j, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,10 +417,13 @@ void ledTask(void *parameter) {
|
||||||
|
|
||||||
delay(900);
|
delay(900);
|
||||||
|
|
||||||
for (int i = NEOPIXEL_COUNT; i--; i > 0) {
|
for (int i = NEOPIXEL_COUNT; i--; i > 0)
|
||||||
for (int j = NEOPIXEL_COUNT; j--; j > 0) {
|
{
|
||||||
|
for (int j = NEOPIXEL_COUNT; j--; j > 0)
|
||||||
|
{
|
||||||
uint32_t c = pixels.Color(0, 0, 0);
|
uint32_t c = pixels.Color(0, 0, 0);
|
||||||
if (i == j) c = pixels.Color(0, 255, 0);
|
if (i == j)
|
||||||
|
c = pixels.Color(0, 255, 0);
|
||||||
|
|
||||||
pixels.setPixelColor(j, c);
|
pixels.setPixelColor(j, c);
|
||||||
}
|
}
|
||||||
|
@ -130,7 +440,8 @@ void ledTask(void *parameter) {
|
||||||
|
|
||||||
// revert to previous state unless power test
|
// revert to previous state unless power test
|
||||||
|
|
||||||
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
|
for (int i = 0; i < NEOPIXEL_COUNT; i++)
|
||||||
|
{
|
||||||
pixels.setPixelColor(i, oldLights[i]);
|
pixels.setPixelColor(i, oldLights[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,14 +451,17 @@ void ledTask(void *parameter) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupLeds() {
|
void setupLeds()
|
||||||
|
{
|
||||||
pixels.begin();
|
pixels.begin();
|
||||||
pixels.setBrightness(preferences.getUInt("ledBrightness", 128));
|
pixels.setBrightness(preferences.getUInt("ledBrightness", DEFAULT_LED_BRIGHTNESS));
|
||||||
pixels.clear();
|
pixels.clear();
|
||||||
pixels.show();
|
pixels.show();
|
||||||
setupLedTask();
|
setupLedTask();
|
||||||
if (preferences.getBool("ledTestOnPower", true)) {
|
if (preferences.getBool("ledTestOnPower", DEFAULT_LED_TEST_ON_POWER))
|
||||||
while (!ledTaskQueue) {
|
{
|
||||||
|
while (!ledTaskQueue)
|
||||||
|
{
|
||||||
delay(1);
|
delay(1);
|
||||||
// wait until queue is available
|
// wait until queue is available
|
||||||
}
|
}
|
||||||
|
@ -155,14 +469,17 @@ void setupLeds() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupLedTask() {
|
void setupLedTask()
|
||||||
|
{
|
||||||
ledTaskQueue = xQueueCreate(5, sizeof(uint));
|
ledTaskQueue = xQueueCreate(5, sizeof(uint));
|
||||||
|
|
||||||
xTaskCreate(ledTask, "LedTask", 2048, NULL, tskIDLE_PRIORITY, &ledTaskHandle);
|
xTaskCreate(ledTask, "LedTask", 2048, NULL, 10, &ledTaskHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void blinkDelay(int d, int times) {
|
void blinkDelay(int d, int times)
|
||||||
for (int j = 0; j < times; j++) {
|
{
|
||||||
|
for (int j = 0; j < times; j++)
|
||||||
|
{
|
||||||
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
|
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
|
||||||
pixels.setPixelColor(1, pixels.Color(0, 255, 0));
|
pixels.setPixelColor(1, pixels.Color(0, 255, 0));
|
||||||
pixels.setPixelColor(2, pixels.Color(255, 0, 0));
|
pixels.setPixelColor(2, pixels.Color(255, 0, 0));
|
||||||
|
@ -181,9 +498,12 @@ void blinkDelay(int d, int times) {
|
||||||
pixels.show();
|
pixels.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void blinkDelayColor(int d, int times, uint r, uint g, uint b) {
|
void blinkDelayColor(int d, int times, uint r, uint g, uint b)
|
||||||
for (int j = 0; j < times; j++) {
|
{
|
||||||
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
|
for (int j = 0; j < times; j++)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NEOPIXEL_COUNT; i++)
|
||||||
|
{
|
||||||
pixels.setPixelColor(i, pixels.Color(r, g, b));
|
pixels.setPixelColor(i, pixels.Color(r, g, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,15 +518,19 @@ void blinkDelayColor(int d, int times, uint r, uint g, uint b) {
|
||||||
pixels.show();
|
pixels.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2) {
|
void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2)
|
||||||
for (int j = 0; j < times; j++) {
|
{
|
||||||
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
|
for (int j = 0; j < times; j++)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NEOPIXEL_COUNT; i++)
|
||||||
|
{
|
||||||
pixels.setPixelColor(i, c1);
|
pixels.setPixelColor(i, c1);
|
||||||
}
|
}
|
||||||
pixels.show();
|
pixels.show();
|
||||||
vTaskDelay(pdMS_TO_TICKS(d));
|
vTaskDelay(pdMS_TO_TICKS(d));
|
||||||
|
|
||||||
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
|
for (int i = 0; i < NEOPIXEL_COUNT; i++)
|
||||||
|
{
|
||||||
pixels.setPixelColor(i, c2);
|
pixels.setPixelColor(i, c2);
|
||||||
}
|
}
|
||||||
pixels.show();
|
pixels.show();
|
||||||
|
@ -216,7 +540,8 @@ void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2) {
|
||||||
pixels.show();
|
pixels.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearLeds() {
|
void clearLeds()
|
||||||
|
{
|
||||||
preferences.putBool("ledStatus", false);
|
preferences.putBool("ledStatus", false);
|
||||||
pixels.clear();
|
pixels.clear();
|
||||||
pixels.show();
|
pixels.show();
|
||||||
|
@ -224,24 +549,31 @@ void clearLeds() {
|
||||||
|
|
||||||
void setLights(int r, int g, int b) { setLights(pixels.Color(r, g, b)); }
|
void setLights(int r, int g, int b) { setLights(pixels.Color(r, g, b)); }
|
||||||
|
|
||||||
void setLights(uint32_t color) {
|
void setLights(uint32_t color)
|
||||||
|
{
|
||||||
bool ledStatus = true;
|
bool ledStatus = true;
|
||||||
|
|
||||||
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
|
for (int i = 0; i < NEOPIXEL_COUNT; i++)
|
||||||
|
{
|
||||||
pixels.setPixelColor(i, color);
|
pixels.setPixelColor(i, color);
|
||||||
}
|
}
|
||||||
pixels.show();
|
pixels.show();
|
||||||
|
|
||||||
if (color == pixels.Color(0, 0, 0)) {
|
if (color == pixels.Color(0, 0, 0))
|
||||||
|
{
|
||||||
ledStatus = false;
|
ledStatus = false;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
saveLedState();
|
saveLedState();
|
||||||
}
|
}
|
||||||
preferences.putBool("ledStatus", ledStatus);
|
preferences.putBool("ledStatus", ledStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveLedState() {
|
void saveLedState()
|
||||||
for (int i = 0; i < pixels.numPixels(); i++) {
|
{
|
||||||
|
for (int i = 0; i < pixels.numPixels(); i++)
|
||||||
|
{
|
||||||
int pixelColor = pixels.getPixelColor(i);
|
int pixelColor = pixels.getPixelColor(i);
|
||||||
char key[12];
|
char key[12];
|
||||||
snprintf(key, 12, "%s%d", "ledColor_", i);
|
snprintf(key, 12, "%s%d", "ledColor_", i);
|
||||||
|
@ -251,8 +583,10 @@ void saveLedState() {
|
||||||
xTaskNotifyGive(eventSourceTaskHandle);
|
xTaskNotifyGive(eventSourceTaskHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void restoreLedState() {
|
void restoreLedState()
|
||||||
for (int i = 0; i < pixels.numPixels(); i++) {
|
{
|
||||||
|
for (int i = 0; i < pixels.numPixels(); i++)
|
||||||
|
{
|
||||||
char key[12];
|
char key[12];
|
||||||
snprintf(key, 12, "%s%d", "ledColor_", i);
|
snprintf(key, 12, "%s%d", "ledColor_", i);
|
||||||
uint pixelColor = preferences.getUInt(key, pixels.Color(0, 0, 0));
|
uint pixelColor = preferences.getUInt(key, pixels.Color(0, 0, 0));
|
||||||
|
@ -264,8 +598,10 @@ void restoreLedState() {
|
||||||
|
|
||||||
QueueHandle_t getLedTaskQueue() { return ledTaskQueue; }
|
QueueHandle_t getLedTaskQueue() { return ledTaskQueue; }
|
||||||
|
|
||||||
bool queueLedEffect(uint effect) {
|
bool queueLedEffect(uint effect)
|
||||||
if (ledTaskQueue == NULL) {
|
{
|
||||||
|
if (ledTaskQueue == NULL)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,13 +609,15 @@ bool queueLedEffect(uint effect) {
|
||||||
xQueueSend(ledTaskQueue, &flashType, portMAX_DELAY);
|
xQueueSend(ledTaskQueue, &flashType, portMAX_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ledRainbow(int wait) {
|
void ledRainbow(int wait)
|
||||||
|
{
|
||||||
// Hue of first pixel runs 5 complete loops through the color wheel.
|
// Hue of first pixel runs 5 complete loops through the color wheel.
|
||||||
// Color wheel has a range of 65536 but it's OK if we roll over, so
|
// Color wheel has a range of 65536 but it's OK if we roll over, so
|
||||||
// just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
|
// just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
|
||||||
// means we'll make 5*65536/256 = 1280 passes through this loop:
|
// means we'll make 5*65536/256 = 1280 passes through this loop:
|
||||||
for (long firstPixelHue = 0; firstPixelHue < 5 * 65536;
|
for (long firstPixelHue = 0; firstPixelHue < 5 * 65536;
|
||||||
firstPixelHue += 256) {
|
firstPixelHue += 256)
|
||||||
|
{
|
||||||
// strip.rainbow() can take a single argument (first pixel hue) or
|
// strip.rainbow() can take a single argument (first pixel hue) or
|
||||||
// optionally a few extras: number of rainbow repetitions (default 1),
|
// optionally a few extras: number of rainbow repetitions (default 1),
|
||||||
// saturation and value (brightness) (both 0-255, similar to the
|
// saturation and value (brightness) (both 0-255, similar to the
|
||||||
|
@ -294,12 +632,16 @@ void ledRainbow(int wait) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ledTheaterChase(uint32_t color, int wait) {
|
void ledTheaterChase(uint32_t color, int wait)
|
||||||
for (int a = 0; a < 10; a++) { // Repeat 10 times...
|
{
|
||||||
for (int b = 0; b < 3; b++) { // 'b' counts from 0 to 2...
|
for (int a = 0; a < 10; a++)
|
||||||
|
{ // Repeat 10 times...
|
||||||
|
for (int b = 0; b < 3; b++)
|
||||||
|
{ // 'b' counts from 0 to 2...
|
||||||
pixels.clear(); // Set all pixels in RAM to 0 (off)
|
pixels.clear(); // Set all pixels in RAM to 0 (off)
|
||||||
// 'c' counts up from 'b' to end of strip in steps of 3...
|
// 'c' counts up from 'b' to end of strip in steps of 3...
|
||||||
for (int c = b; c < pixels.numPixels(); c += 3) {
|
for (int c = b; c < pixels.numPixels(); c += 3)
|
||||||
|
{
|
||||||
pixels.setPixelColor(c, color); // Set pixel 'c' to value 'color'
|
pixels.setPixelColor(c, color); // Set pixel 'c' to value 'color'
|
||||||
}
|
}
|
||||||
pixels.show(); // Update strip with new contents
|
pixels.show(); // Update strip with new contents
|
||||||
|
@ -308,13 +650,17 @@ void ledTheaterChase(uint32_t color, int wait) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ledTheaterChaseRainbow(int wait) {
|
void ledTheaterChaseRainbow(int wait)
|
||||||
|
{
|
||||||
int firstPixelHue = 0; // First pixel starts at red (hue 0)
|
int firstPixelHue = 0; // First pixel starts at red (hue 0)
|
||||||
for (int a = 0; a < 30; a++) { // Repeat 30 times...
|
for (int a = 0; a < 30; a++)
|
||||||
for (int b = 0; b < 3; b++) { // 'b' counts from 0 to 2...
|
{ // Repeat 30 times...
|
||||||
|
for (int b = 0; b < 3; b++)
|
||||||
|
{ // 'b' counts from 0 to 2...
|
||||||
pixels.clear(); // Set all pixels in RAM to 0 (off)
|
pixels.clear(); // Set all pixels in RAM to 0 (off)
|
||||||
// 'c' counts up from 'b' to end of strip in increments of 3...
|
// 'c' counts up from 'b' to end of strip in increments of 3...
|
||||||
for (int c = b; c < pixels.numPixels(); c += 3) {
|
for (int c = b; c < pixels.numPixels(); c += 3)
|
||||||
|
{
|
||||||
// hue of pixel 'c' is offset by an amount to make one full
|
// hue of pixel 'c' is offset by an amount to make one full
|
||||||
// revolution of the color wheel (range 65536) along the length
|
// revolution of the color wheel (range 65536) along the length
|
||||||
// of the strip (strip.numPixels() steps):
|
// of the strip (strip.numPixels() steps):
|
||||||
|
@ -329,4 +675,29 @@ 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; }
|
Adafruit_NeoPixel getPixels() { return pixels; }
|
|
@ -28,6 +28,7 @@ const int LED_EFFECT_WIFI_CONNECT_ERROR = 102;
|
||||||
const int LED_EFFECT_WIFI_CONNECT_SUCCESS = 103;
|
const int LED_EFFECT_WIFI_CONNECT_SUCCESS = 103;
|
||||||
const int LED_EFFECT_WIFI_ERASE_SETTINGS = 104;
|
const int LED_EFFECT_WIFI_ERASE_SETTINGS = 104;
|
||||||
|
|
||||||
|
|
||||||
const int LED_PROGRESS_25 = 200;
|
const int LED_PROGRESS_25 = 200;
|
||||||
const int LED_PROGRESS_50 = 201;
|
const int LED_PROGRESS_50 = 201;
|
||||||
const int LED_PROGRESS_75 = 202;
|
const int LED_PROGRESS_75 = 202;
|
||||||
|
@ -36,6 +37,9 @@ const int LED_PROGRESS_100 = 203;
|
||||||
const int LED_DATA_PRICE_ERROR = 300;
|
const int LED_DATA_PRICE_ERROR = 300;
|
||||||
const int LED_DATA_BLOCK_ERROR = 301;
|
const int LED_DATA_BLOCK_ERROR = 301;
|
||||||
|
|
||||||
|
const int LED_EFFECT_NOSTR_ZAP = 400;
|
||||||
|
|
||||||
|
const int LED_FLASH_IDENTIFY = 990;
|
||||||
const int LED_POWER_TEST = 999;
|
const int LED_POWER_TEST = 999;
|
||||||
extern TaskHandle_t ledTaskHandle;
|
extern TaskHandle_t ledTaskHandle;
|
||||||
extern Adafruit_NeoPixel pixels;
|
extern Adafruit_NeoPixel pixels;
|
||||||
|
@ -57,3 +61,25 @@ void ledRainbow(int wait);
|
||||||
void ledTheaterChaseRainbow(int wait);
|
void ledTheaterChaseRainbow(int wait);
|
||||||
void ledTheaterChase(uint32_t color, int wait);
|
void ledTheaterChase(uint32_t color, int wait);
|
||||||
Adafruit_NeoPixel getPixels();
|
Adafruit_NeoPixel getPixels();
|
||||||
|
void lightningStrike();
|
||||||
|
|
||||||
|
#ifdef HAS_FRONTLIGHT
|
||||||
|
void frontlightFlash(int flDelayTime);
|
||||||
|
void frontlightFadeInAll();
|
||||||
|
void frontlightFadeOutAll();
|
||||||
|
void frontlightFadeIn(uint num);
|
||||||
|
void frontlightFadeOut(uint num);
|
||||||
|
|
||||||
|
std::vector<uint16_t> frontlightGetStatus();
|
||||||
|
|
||||||
|
void frontlightSetBrightness(uint brightness);
|
||||||
|
bool frontlightIsOn();
|
||||||
|
|
||||||
|
void frontlightFadeInAll(int flDelayTime);
|
||||||
|
void frontlightFadeInAll(int flDelayTime, bool staggered);
|
||||||
|
void frontlightFadeOutAll(int flDelayTime);
|
||||||
|
void frontlightFadeOutAll(int flDelayTime, bool staggered);
|
||||||
|
|
||||||
|
void frontlightFadeIn(uint num, int flDelayTime);
|
||||||
|
void frontlightFadeOut(uint num, int flDelayTime);
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -1,795 +0,0 @@
|
||||||
/*
|
|
||||||
* The little filesystem
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, The littlefs authors.
|
|
||||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
|
||||||
* SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
*/
|
|
||||||
#ifndef LFS_H
|
|
||||||
#define LFS_H
|
|
||||||
|
|
||||||
#include "lfs_util.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/// Version info ///
|
|
||||||
|
|
||||||
// Software library version
|
|
||||||
// Major (top-nibble), incremented on backwards incompatible changes
|
|
||||||
// Minor (bottom-nibble), incremented on feature additions
|
|
||||||
#define LFS_VERSION 0x00020009
|
|
||||||
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16))
|
|
||||||
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))
|
|
||||||
|
|
||||||
// Version of On-disk data structures
|
|
||||||
// Major (top-nibble), incremented on backwards incompatible changes
|
|
||||||
// Minor (bottom-nibble), incremented on feature additions
|
|
||||||
#define LFS_DISK_VERSION 0x00020001
|
|
||||||
#define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16))
|
|
||||||
#define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0))
|
|
||||||
|
|
||||||
|
|
||||||
/// Definitions ///
|
|
||||||
|
|
||||||
// Type definitions
|
|
||||||
typedef uint32_t lfs_size_t;
|
|
||||||
typedef uint32_t lfs_off_t;
|
|
||||||
|
|
||||||
typedef int32_t lfs_ssize_t;
|
|
||||||
typedef int32_t lfs_soff_t;
|
|
||||||
|
|
||||||
typedef uint32_t lfs_block_t;
|
|
||||||
|
|
||||||
// Maximum name size in bytes, may be redefined to reduce the size of the
|
|
||||||
// info struct. Limited to <= 1022. Stored in superblock and must be
|
|
||||||
// respected by other littlefs drivers.
|
|
||||||
#ifndef LFS_NAME_MAX
|
|
||||||
#define LFS_NAME_MAX 255
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Maximum size of a file in bytes, may be redefined to limit to support other
|
|
||||||
// drivers. Limited on disk to <= 2147483647. Stored in superblock and must be
|
|
||||||
// respected by other littlefs drivers.
|
|
||||||
#ifndef LFS_FILE_MAX
|
|
||||||
#define LFS_FILE_MAX 2147483647
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Maximum size of custom attributes in bytes, may be redefined, but there is
|
|
||||||
// no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022.
|
|
||||||
#ifndef LFS_ATTR_MAX
|
|
||||||
#define LFS_ATTR_MAX 1022
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Possible error codes, these are negative to allow
|
|
||||||
// valid positive return values
|
|
||||||
enum lfs_error {
|
|
||||||
LFS_ERR_OK = 0, // No error
|
|
||||||
LFS_ERR_IO = -5, // Error during device operation
|
|
||||||
LFS_ERR_CORRUPT = -84, // Corrupted
|
|
||||||
LFS_ERR_NOENT = -2, // No directory entry
|
|
||||||
LFS_ERR_EXIST = -17, // Entry already exists
|
|
||||||
LFS_ERR_NOTDIR = -20, // Entry is not a dir
|
|
||||||
LFS_ERR_ISDIR = -21, // Entry is a dir
|
|
||||||
LFS_ERR_NOTEMPTY = -39, // Dir is not empty
|
|
||||||
LFS_ERR_BADF = -9, // Bad file number
|
|
||||||
LFS_ERR_FBIG = -27, // File too large
|
|
||||||
LFS_ERR_INVAL = -22, // Invalid parameter
|
|
||||||
LFS_ERR_NOSPC = -28, // No space left on device
|
|
||||||
LFS_ERR_NOMEM = -12, // No more memory available
|
|
||||||
LFS_ERR_NOATTR = -61, // No data/attr available
|
|
||||||
LFS_ERR_NAMETOOLONG = -36, // File name too long
|
|
||||||
};
|
|
||||||
|
|
||||||
// File types
|
|
||||||
enum lfs_type {
|
|
||||||
// file types
|
|
||||||
LFS_TYPE_REG = 0x001,
|
|
||||||
LFS_TYPE_DIR = 0x002,
|
|
||||||
|
|
||||||
// internally used types
|
|
||||||
LFS_TYPE_SPLICE = 0x400,
|
|
||||||
LFS_TYPE_NAME = 0x000,
|
|
||||||
LFS_TYPE_STRUCT = 0x200,
|
|
||||||
LFS_TYPE_USERATTR = 0x300,
|
|
||||||
LFS_TYPE_FROM = 0x100,
|
|
||||||
LFS_TYPE_TAIL = 0x600,
|
|
||||||
LFS_TYPE_GLOBALS = 0x700,
|
|
||||||
LFS_TYPE_CRC = 0x500,
|
|
||||||
|
|
||||||
// internally used type specializations
|
|
||||||
LFS_TYPE_CREATE = 0x401,
|
|
||||||
LFS_TYPE_DELETE = 0x4ff,
|
|
||||||
LFS_TYPE_SUPERBLOCK = 0x0ff,
|
|
||||||
LFS_TYPE_DIRSTRUCT = 0x200,
|
|
||||||
LFS_TYPE_CTZSTRUCT = 0x202,
|
|
||||||
LFS_TYPE_INLINESTRUCT = 0x201,
|
|
||||||
LFS_TYPE_SOFTTAIL = 0x600,
|
|
||||||
LFS_TYPE_HARDTAIL = 0x601,
|
|
||||||
LFS_TYPE_MOVESTATE = 0x7ff,
|
|
||||||
LFS_TYPE_CCRC = 0x500,
|
|
||||||
LFS_TYPE_FCRC = 0x5ff,
|
|
||||||
|
|
||||||
// internal chip sources
|
|
||||||
LFS_FROM_NOOP = 0x000,
|
|
||||||
LFS_FROM_MOVE = 0x101,
|
|
||||||
LFS_FROM_USERATTRS = 0x102,
|
|
||||||
};
|
|
||||||
|
|
||||||
// File open flags
|
|
||||||
enum lfs_open_flags {
|
|
||||||
// open flags
|
|
||||||
LFS_O_RDONLY = 1, // Open a file as read only
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
LFS_O_WRONLY = 2, // Open a file as write only
|
|
||||||
LFS_O_RDWR = 3, // Open a file as read and write
|
|
||||||
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
|
|
||||||
LFS_O_EXCL = 0x0200, // Fail if a file already exists
|
|
||||||
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
|
|
||||||
LFS_O_APPEND = 0x0800, // Move to end of file on every write
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// internally used flags
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
LFS_F_DIRTY = 0x010000, // File does not match storage
|
|
||||||
LFS_F_WRITING = 0x020000, // File has been written since last flush
|
|
||||||
#endif
|
|
||||||
LFS_F_READING = 0x040000, // File has been read since last flush
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
LFS_F_ERRED = 0x080000, // An error occurred during write
|
|
||||||
#endif
|
|
||||||
LFS_F_INLINE = 0x100000, // Currently inlined in directory entry
|
|
||||||
};
|
|
||||||
|
|
||||||
// File seek flags
|
|
||||||
enum lfs_whence_flags {
|
|
||||||
LFS_SEEK_SET = 0, // Seek relative to an absolute position
|
|
||||||
LFS_SEEK_CUR = 1, // Seek relative to the current file position
|
|
||||||
LFS_SEEK_END = 2, // Seek relative to the end of the file
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Configuration provided during initialization of the littlefs
|
|
||||||
struct lfs_config {
|
|
||||||
// Opaque user provided context that can be used to pass
|
|
||||||
// information to the block device operations
|
|
||||||
void *context;
|
|
||||||
|
|
||||||
// Read a region in a block. Negative error codes are propagated
|
|
||||||
// to the user.
|
|
||||||
int (*read)(const struct lfs_config *c, lfs_block_t block,
|
|
||||||
lfs_off_t off, void *buffer, lfs_size_t size);
|
|
||||||
|
|
||||||
// Program a region in a block. The block must have previously
|
|
||||||
// been erased. Negative error codes are propagated to the user.
|
|
||||||
// May return LFS_ERR_CORRUPT if the block should be considered bad.
|
|
||||||
int (*prog)(const struct lfs_config *c, lfs_block_t block,
|
|
||||||
lfs_off_t off, const void *buffer, lfs_size_t size);
|
|
||||||
|
|
||||||
// Erase a block. A block must be erased before being programmed.
|
|
||||||
// The state of an erased block is undefined. Negative error codes
|
|
||||||
// are propagated to the user.
|
|
||||||
// May return LFS_ERR_CORRUPT if the block should be considered bad.
|
|
||||||
int (*erase)(const struct lfs_config *c, lfs_block_t block);
|
|
||||||
|
|
||||||
// Sync the state of the underlying block device. Negative error codes
|
|
||||||
// are propagated to the user.
|
|
||||||
int (*sync)(const struct lfs_config *c);
|
|
||||||
|
|
||||||
#ifdef LFS_THREADSAFE
|
|
||||||
// Lock the underlying block device. Negative error codes
|
|
||||||
// are propagated to the user.
|
|
||||||
int (*lock)(const struct lfs_config *c);
|
|
||||||
|
|
||||||
// Unlock the underlying block device. Negative error codes
|
|
||||||
// are propagated to the user.
|
|
||||||
int (*unlock)(const struct lfs_config *c);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Minimum size of a block read in bytes. All read operations will be a
|
|
||||||
// multiple of this value.
|
|
||||||
lfs_size_t read_size;
|
|
||||||
|
|
||||||
// Minimum size of a block program in bytes. All program operations will be
|
|
||||||
// a multiple of this value.
|
|
||||||
lfs_size_t prog_size;
|
|
||||||
|
|
||||||
// Size of an erasable block in bytes. This does not impact ram consumption
|
|
||||||
// and may be larger than the physical erase size. However, non-inlined
|
|
||||||
// files take up at minimum one block. Must be a multiple of the read and
|
|
||||||
// program sizes.
|
|
||||||
lfs_size_t block_size;
|
|
||||||
|
|
||||||
// Number of erasable blocks on the device.
|
|
||||||
lfs_size_t block_count;
|
|
||||||
|
|
||||||
// Number of erase cycles before littlefs evicts metadata logs and moves
|
|
||||||
// the metadata to another block. Suggested values are in the
|
|
||||||
// range 100-1000, with large values having better performance at the cost
|
|
||||||
// of less consistent wear distribution.
|
|
||||||
//
|
|
||||||
// Set to -1 to disable block-level wear-leveling.
|
|
||||||
int32_t block_cycles;
|
|
||||||
|
|
||||||
// Size of block caches in bytes. Each cache buffers a portion of a block in
|
|
||||||
// RAM. The littlefs needs a read cache, a program cache, and one additional
|
|
||||||
// cache per file. Larger caches can improve performance by storing more
|
|
||||||
// data and reducing the number of disk accesses. Must be a multiple of the
|
|
||||||
// read and program sizes, and a factor of the block size.
|
|
||||||
lfs_size_t cache_size;
|
|
||||||
|
|
||||||
// Size of the lookahead buffer in bytes. A larger lookahead buffer
|
|
||||||
// increases the number of blocks found during an allocation pass. The
|
|
||||||
// lookahead buffer is stored as a compact bitmap, so each byte of RAM
|
|
||||||
// can track 8 blocks.
|
|
||||||
lfs_size_t lookahead_size;
|
|
||||||
|
|
||||||
// Threshold for metadata compaction during lfs_fs_gc in bytes. Metadata
|
|
||||||
// pairs that exceed this threshold will be compacted during lfs_fs_gc.
|
|
||||||
// Defaults to ~88% block_size when zero, though the default may change
|
|
||||||
// in the future.
|
|
||||||
//
|
|
||||||
// Note this only affects lfs_fs_gc. Normal compactions still only occur
|
|
||||||
// when full.
|
|
||||||
//
|
|
||||||
// Set to -1 to disable metadata compaction during lfs_fs_gc.
|
|
||||||
lfs_size_t compact_thresh;
|
|
||||||
|
|
||||||
// Optional statically allocated read buffer. Must be cache_size.
|
|
||||||
// By default lfs_malloc is used to allocate this buffer.
|
|
||||||
void *read_buffer;
|
|
||||||
|
|
||||||
// Optional statically allocated program buffer. Must be cache_size.
|
|
||||||
// By default lfs_malloc is used to allocate this buffer.
|
|
||||||
void *prog_buffer;
|
|
||||||
|
|
||||||
// Optional statically allocated lookahead buffer. Must be lookahead_size.
|
|
||||||
// By default lfs_malloc is used to allocate this buffer.
|
|
||||||
void *lookahead_buffer;
|
|
||||||
|
|
||||||
// Optional upper limit on length of file names in bytes. No downside for
|
|
||||||
// larger names except the size of the info struct which is controlled by
|
|
||||||
// the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in
|
|
||||||
// superblock and must be respected by other littlefs drivers.
|
|
||||||
lfs_size_t name_max;
|
|
||||||
|
|
||||||
// Optional upper limit on files in bytes. No downside for larger files
|
|
||||||
// but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored
|
|
||||||
// in superblock and must be respected by other littlefs drivers.
|
|
||||||
lfs_size_t file_max;
|
|
||||||
|
|
||||||
// Optional upper limit on custom attributes in bytes. No downside for
|
|
||||||
// larger attributes size but must be <= LFS_ATTR_MAX. Defaults to
|
|
||||||
// LFS_ATTR_MAX when zero.
|
|
||||||
lfs_size_t attr_max;
|
|
||||||
|
|
||||||
// Optional upper limit on total space given to metadata pairs in bytes. On
|
|
||||||
// devices with large blocks (e.g. 128kB) setting this to a low size (2-8kB)
|
|
||||||
// can help bound the metadata compaction time. Must be <= block_size.
|
|
||||||
// Defaults to block_size when zero.
|
|
||||||
lfs_size_t metadata_max;
|
|
||||||
|
|
||||||
// Optional upper limit on inlined files in bytes. Inlined files live in
|
|
||||||
// metadata and decrease storage requirements, but may be limited to
|
|
||||||
// improve metadata-related performance. Must be <= cache_size, <=
|
|
||||||
// attr_max, and <= block_size/8. Defaults to the largest possible
|
|
||||||
// inline_max when zero.
|
|
||||||
//
|
|
||||||
// Set to -1 to disable inlined files.
|
|
||||||
lfs_size_t inline_max;
|
|
||||||
|
|
||||||
#ifdef LFS_MULTIVERSION
|
|
||||||
// On-disk version to use when writing in the form of 16-bit major version
|
|
||||||
// + 16-bit minor version. This limiting metadata to what is supported by
|
|
||||||
// older minor versions. Note that some features will be lost. Defaults to
|
|
||||||
// to the most recent minor version when zero.
|
|
||||||
uint32_t disk_version;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
// File info structure
|
|
||||||
struct lfs_info {
|
|
||||||
// Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR
|
|
||||||
uint8_t type;
|
|
||||||
|
|
||||||
// Size of the file, only valid for REG files. Limited to 32-bits.
|
|
||||||
lfs_size_t size;
|
|
||||||
|
|
||||||
// Name of the file stored as a null-terminated string. Limited to
|
|
||||||
// LFS_NAME_MAX+1, which can be changed by redefining LFS_NAME_MAX to
|
|
||||||
// reduce RAM. LFS_NAME_MAX is stored in superblock and must be
|
|
||||||
// respected by other littlefs drivers.
|
|
||||||
char name[LFS_NAME_MAX+1];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Filesystem info structure
|
|
||||||
struct lfs_fsinfo {
|
|
||||||
// On-disk version.
|
|
||||||
uint32_t disk_version;
|
|
||||||
|
|
||||||
// Size of a logical block in bytes.
|
|
||||||
lfs_size_t block_size;
|
|
||||||
|
|
||||||
// Number of logical blocks in filesystem.
|
|
||||||
lfs_size_t block_count;
|
|
||||||
|
|
||||||
// Upper limit on the length of file names in bytes.
|
|
||||||
lfs_size_t name_max;
|
|
||||||
|
|
||||||
// Upper limit on the size of files in bytes.
|
|
||||||
lfs_size_t file_max;
|
|
||||||
|
|
||||||
// Upper limit on the size of custom attributes in bytes.
|
|
||||||
lfs_size_t attr_max;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Custom attribute structure, used to describe custom attributes
|
|
||||||
// committed atomically during file writes.
|
|
||||||
struct lfs_attr {
|
|
||||||
// 8-bit type of attribute, provided by user and used to
|
|
||||||
// identify the attribute
|
|
||||||
uint8_t type;
|
|
||||||
|
|
||||||
// Pointer to buffer containing the attribute
|
|
||||||
void *buffer;
|
|
||||||
|
|
||||||
// Size of attribute in bytes, limited to LFS_ATTR_MAX
|
|
||||||
lfs_size_t size;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Optional configuration provided during lfs_file_opencfg
|
|
||||||
struct lfs_file_config {
|
|
||||||
// Optional statically allocated file buffer. Must be cache_size.
|
|
||||||
// By default lfs_malloc is used to allocate this buffer.
|
|
||||||
void *buffer;
|
|
||||||
|
|
||||||
// Optional list of custom attributes related to the file. If the file
|
|
||||||
// is opened with read access, these attributes will be read from disk
|
|
||||||
// during the open call. If the file is opened with write access, the
|
|
||||||
// attributes will be written to disk every file sync or close. This
|
|
||||||
// write occurs atomically with update to the file's contents.
|
|
||||||
//
|
|
||||||
// Custom attributes are uniquely identified by an 8-bit type and limited
|
|
||||||
// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller
|
|
||||||
// than the buffer, it will be padded with zeros. If the stored attribute
|
|
||||||
// is larger, then it will be silently truncated. If the attribute is not
|
|
||||||
// found, it will be created implicitly.
|
|
||||||
struct lfs_attr *attrs;
|
|
||||||
|
|
||||||
// Number of custom attributes in the list
|
|
||||||
lfs_size_t attr_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// internal littlefs data structures ///
|
|
||||||
typedef struct lfs_cache {
|
|
||||||
lfs_block_t block;
|
|
||||||
lfs_off_t off;
|
|
||||||
lfs_size_t size;
|
|
||||||
uint8_t *buffer;
|
|
||||||
} lfs_cache_t;
|
|
||||||
|
|
||||||
typedef struct lfs_mdir {
|
|
||||||
lfs_block_t pair[2];
|
|
||||||
uint32_t rev;
|
|
||||||
lfs_off_t off;
|
|
||||||
uint32_t etag;
|
|
||||||
uint16_t count;
|
|
||||||
bool erased;
|
|
||||||
bool split;
|
|
||||||
lfs_block_t tail[2];
|
|
||||||
} lfs_mdir_t;
|
|
||||||
|
|
||||||
// littlefs directory type
|
|
||||||
typedef struct lfs_dir {
|
|
||||||
struct lfs_dir *next;
|
|
||||||
uint16_t id;
|
|
||||||
uint8_t type;
|
|
||||||
lfs_mdir_t m;
|
|
||||||
|
|
||||||
lfs_off_t pos;
|
|
||||||
lfs_block_t head[2];
|
|
||||||
} lfs_dir_t;
|
|
||||||
|
|
||||||
// littlefs file type
|
|
||||||
typedef struct lfs_file {
|
|
||||||
struct lfs_file *next;
|
|
||||||
uint16_t id;
|
|
||||||
uint8_t type;
|
|
||||||
lfs_mdir_t m;
|
|
||||||
|
|
||||||
struct lfs_ctz {
|
|
||||||
lfs_block_t head;
|
|
||||||
lfs_size_t size;
|
|
||||||
} ctz;
|
|
||||||
|
|
||||||
uint32_t flags;
|
|
||||||
lfs_off_t pos;
|
|
||||||
lfs_block_t block;
|
|
||||||
lfs_off_t off;
|
|
||||||
lfs_cache_t cache;
|
|
||||||
|
|
||||||
const struct lfs_file_config *cfg;
|
|
||||||
} lfs_file_t;
|
|
||||||
|
|
||||||
typedef struct lfs_superblock {
|
|
||||||
uint32_t version;
|
|
||||||
lfs_size_t block_size;
|
|
||||||
lfs_size_t block_count;
|
|
||||||
lfs_size_t name_max;
|
|
||||||
lfs_size_t file_max;
|
|
||||||
lfs_size_t attr_max;
|
|
||||||
} lfs_superblock_t;
|
|
||||||
|
|
||||||
typedef struct lfs_gstate {
|
|
||||||
uint32_t tag;
|
|
||||||
lfs_block_t pair[2];
|
|
||||||
} lfs_gstate_t;
|
|
||||||
|
|
||||||
// The littlefs filesystem type
|
|
||||||
typedef struct lfs {
|
|
||||||
lfs_cache_t rcache;
|
|
||||||
lfs_cache_t pcache;
|
|
||||||
|
|
||||||
lfs_block_t root[2];
|
|
||||||
struct lfs_mlist {
|
|
||||||
struct lfs_mlist *next;
|
|
||||||
uint16_t id;
|
|
||||||
uint8_t type;
|
|
||||||
lfs_mdir_t m;
|
|
||||||
} *mlist;
|
|
||||||
uint32_t seed;
|
|
||||||
|
|
||||||
lfs_gstate_t gstate;
|
|
||||||
lfs_gstate_t gdisk;
|
|
||||||
lfs_gstate_t gdelta;
|
|
||||||
|
|
||||||
struct lfs_lookahead {
|
|
||||||
lfs_block_t start;
|
|
||||||
lfs_block_t size;
|
|
||||||
lfs_block_t next;
|
|
||||||
lfs_block_t ckpoint;
|
|
||||||
uint8_t *buffer;
|
|
||||||
} lookahead;
|
|
||||||
|
|
||||||
const struct lfs_config *cfg;
|
|
||||||
lfs_size_t block_count;
|
|
||||||
lfs_size_t name_max;
|
|
||||||
lfs_size_t file_max;
|
|
||||||
lfs_size_t attr_max;
|
|
||||||
lfs_size_t inline_max;
|
|
||||||
|
|
||||||
#ifdef LFS_MIGRATE
|
|
||||||
struct lfs1 *lfs1;
|
|
||||||
#endif
|
|
||||||
} lfs_t;
|
|
||||||
|
|
||||||
|
|
||||||
/// Filesystem functions ///
|
|
||||||
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
// Format a block device with the littlefs
|
|
||||||
//
|
|
||||||
// Requires a littlefs object and config struct. This clobbers the littlefs
|
|
||||||
// object, and does not leave the filesystem mounted. The config struct must
|
|
||||||
// be zeroed for defaults and backwards compatibility.
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_format(lfs_t *lfs, const struct lfs_config *config);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Mounts a littlefs
|
|
||||||
//
|
|
||||||
// Requires a littlefs object and config struct. Multiple filesystems
|
|
||||||
// may be mounted simultaneously with multiple littlefs objects. Both
|
|
||||||
// lfs and config must be allocated while mounted. The config struct must
|
|
||||||
// be zeroed for defaults and backwards compatibility.
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_mount(lfs_t *lfs, const struct lfs_config *config);
|
|
||||||
|
|
||||||
// Unmounts a littlefs
|
|
||||||
//
|
|
||||||
// Does nothing besides releasing any allocated resources.
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_unmount(lfs_t *lfs);
|
|
||||||
|
|
||||||
/// General operations ///
|
|
||||||
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
// Removes a file or directory
|
|
||||||
//
|
|
||||||
// If removing a directory, the directory must be empty.
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_remove(lfs_t *lfs, const char *path);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
// Rename or move a file or directory
|
|
||||||
//
|
|
||||||
// If the destination exists, it must match the source in type.
|
|
||||||
// If the destination is a directory, the directory must be empty.
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Find info about a file or directory
|
|
||||||
//
|
|
||||||
// Fills out the info structure, based on the specified file or directory.
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);
|
|
||||||
|
|
||||||
// Get a custom attribute
|
|
||||||
//
|
|
||||||
// Custom attributes are uniquely identified by an 8-bit type and limited
|
|
||||||
// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than
|
|
||||||
// the buffer, it will be padded with zeros. If the stored attribute is larger,
|
|
||||||
// then it will be silently truncated. If no attribute is found, the error
|
|
||||||
// LFS_ERR_NOATTR is returned and the buffer is filled with zeros.
|
|
||||||
//
|
|
||||||
// Returns the size of the attribute, or a negative error code on failure.
|
|
||||||
// Note, the returned size is the size of the attribute on disk, irrespective
|
|
||||||
// of the size of the buffer. This can be used to dynamically allocate a buffer
|
|
||||||
// or check for existence.
|
|
||||||
lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
|
|
||||||
uint8_t type, void *buffer, lfs_size_t size);
|
|
||||||
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
// Set custom attributes
|
|
||||||
//
|
|
||||||
// Custom attributes are uniquely identified by an 8-bit type and limited
|
|
||||||
// to LFS_ATTR_MAX bytes. If an attribute is not found, it will be
|
|
||||||
// implicitly created.
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_setattr(lfs_t *lfs, const char *path,
|
|
||||||
uint8_t type, const void *buffer, lfs_size_t size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
// Removes a custom attribute
|
|
||||||
//
|
|
||||||
// If an attribute is not found, nothing happens.
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/// File operations ///
|
|
||||||
|
|
||||||
#ifndef LFS_NO_MALLOC
|
|
||||||
// Open a file
|
|
||||||
//
|
|
||||||
// The mode that the file is opened in is determined by the flags, which
|
|
||||||
// are values from the enum lfs_open_flags that are bitwise-ored together.
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
|
|
||||||
const char *path, int flags);
|
|
||||||
|
|
||||||
// if LFS_NO_MALLOC is defined, lfs_file_open() will fail with LFS_ERR_NOMEM
|
|
||||||
// thus use lfs_file_opencfg() with config.buffer set.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Open a file with extra configuration
|
|
||||||
//
|
|
||||||
// The mode that the file is opened in is determined by the flags, which
|
|
||||||
// are values from the enum lfs_open_flags that are bitwise-ored together.
|
|
||||||
//
|
|
||||||
// The config struct provides additional config options per file as described
|
|
||||||
// above. The config struct must remain allocated while the file is open, and
|
|
||||||
// the config struct must be zeroed for defaults and backwards compatibility.
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
|
|
||||||
const char *path, int flags,
|
|
||||||
const struct lfs_file_config *config);
|
|
||||||
|
|
||||||
// Close a file
|
|
||||||
//
|
|
||||||
// Any pending writes are written out to storage as though
|
|
||||||
// sync had been called and releases any allocated resources.
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_file_close(lfs_t *lfs, lfs_file_t *file);
|
|
||||||
|
|
||||||
// Synchronize a file on storage
|
|
||||||
//
|
|
||||||
// Any pending writes are written out to storage.
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_file_sync(lfs_t *lfs, lfs_file_t *file);
|
|
||||||
|
|
||||||
// Read data from file
|
|
||||||
//
|
|
||||||
// Takes a buffer and size indicating where to store the read data.
|
|
||||||
// Returns the number of bytes read, or a negative error code on failure.
|
|
||||||
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
|
|
||||||
void *buffer, lfs_size_t size);
|
|
||||||
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
// Write data to file
|
|
||||||
//
|
|
||||||
// Takes a buffer and size indicating the data to write. The file will not
|
|
||||||
// actually be updated on the storage until either sync or close is called.
|
|
||||||
//
|
|
||||||
// Returns the number of bytes written, or a negative error code on failure.
|
|
||||||
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
|
|
||||||
const void *buffer, lfs_size_t size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Change the position of the file
|
|
||||||
//
|
|
||||||
// The change in position is determined by the offset and whence flag.
|
|
||||||
// Returns the new position of the file, or a negative error code on failure.
|
|
||||||
lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
|
|
||||||
lfs_soff_t off, int whence);
|
|
||||||
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
// Truncates the size of the file to the specified size
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Return the position of the file
|
|
||||||
//
|
|
||||||
// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR)
|
|
||||||
// Returns the position of the file, or a negative error code on failure.
|
|
||||||
lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file);
|
|
||||||
|
|
||||||
// Change the position of the file to the beginning of the file
|
|
||||||
//
|
|
||||||
// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_SET)
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);
|
|
||||||
|
|
||||||
// Return the size of the file
|
|
||||||
//
|
|
||||||
// Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END)
|
|
||||||
// Returns the size of the file, or a negative error code on failure.
|
|
||||||
lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);
|
|
||||||
|
|
||||||
|
|
||||||
/// Directory operations ///
|
|
||||||
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
// Create a directory
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_mkdir(lfs_t *lfs, const char *path);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Open a directory
|
|
||||||
//
|
|
||||||
// Once open a directory can be used with read to iterate over files.
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path);
|
|
||||||
|
|
||||||
// Close a directory
|
|
||||||
//
|
|
||||||
// Releases any allocated resources.
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir);
|
|
||||||
|
|
||||||
// Read an entry in the directory
|
|
||||||
//
|
|
||||||
// Fills out the info structure, based on the specified file or directory.
|
|
||||||
// Returns a positive value on success, 0 at the end of directory,
|
|
||||||
// or a negative error code on failure.
|
|
||||||
int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info);
|
|
||||||
|
|
||||||
// Change the position of the directory
|
|
||||||
//
|
|
||||||
// The new off must be a value previous returned from tell and specifies
|
|
||||||
// an absolute offset in the directory seek.
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off);
|
|
||||||
|
|
||||||
// Return the position of the directory
|
|
||||||
//
|
|
||||||
// The returned offset is only meant to be consumed by seek and may not make
|
|
||||||
// sense, but does indicate the current position in the directory iteration.
|
|
||||||
//
|
|
||||||
// Returns the position of the directory, or a negative error code on failure.
|
|
||||||
lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir);
|
|
||||||
|
|
||||||
// Change the position of the directory to the beginning of the directory
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
|
|
||||||
|
|
||||||
|
|
||||||
/// Filesystem-level filesystem operations
|
|
||||||
|
|
||||||
// Find on-disk info about the filesystem
|
|
||||||
//
|
|
||||||
// Fills out the fsinfo structure based on the filesystem found on-disk.
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo);
|
|
||||||
|
|
||||||
// Finds the current size of the filesystem
|
|
||||||
//
|
|
||||||
// Note: Result is best effort. If files share COW structures, the returned
|
|
||||||
// size may be larger than the filesystem actually is.
|
|
||||||
//
|
|
||||||
// Returns the number of allocated blocks, or a negative error code on failure.
|
|
||||||
lfs_ssize_t lfs_fs_size(lfs_t *lfs);
|
|
||||||
|
|
||||||
// Traverse through all blocks in use by the filesystem
|
|
||||||
//
|
|
||||||
// The provided callback will be called with each block address that is
|
|
||||||
// currently in use by the filesystem. This can be used to determine which
|
|
||||||
// blocks are in use or how much of the storage is available.
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
|
|
||||||
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
// Attempt to make the filesystem consistent and ready for writing
|
|
||||||
//
|
|
||||||
// Calling this function is not required, consistency will be implicitly
|
|
||||||
// enforced on the first operation that writes to the filesystem, but this
|
|
||||||
// function allows the work to be performed earlier and without other
|
|
||||||
// filesystem changes.
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_fs_mkconsistent(lfs_t *lfs);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
// Attempt any janitorial work
|
|
||||||
//
|
|
||||||
// This currently:
|
|
||||||
// 1. Calls mkconsistent if not already consistent
|
|
||||||
// 2. Compacts metadata > compact_thresh
|
|
||||||
// 3. Populates the block allocator
|
|
||||||
//
|
|
||||||
// Though additional janitorial work may be added in the future.
|
|
||||||
//
|
|
||||||
// Calling this function is not required, but may allow the offloading of
|
|
||||||
// expensive janitorial work to a less time-critical code path.
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure. Accomplishing nothing is not
|
|
||||||
// an error.
|
|
||||||
int lfs_fs_gc(lfs_t *lfs);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
// Grows the filesystem to a new size, updating the superblock with the new
|
|
||||||
// block count.
|
|
||||||
//
|
|
||||||
// Note: This is irreversible.
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
#ifdef LFS_MIGRATE
|
|
||||||
// Attempts to migrate a previous version of littlefs
|
|
||||||
//
|
|
||||||
// Behaves similarly to the lfs_format function. Attempts to mount
|
|
||||||
// the previous version of littlefs and update the filesystem so it can be
|
|
||||||
// mounted with the current version of littlefs.
|
|
||||||
//
|
|
||||||
// Requires a littlefs object and config struct. This clobbers the littlefs
|
|
||||||
// object, and does not leave the filesystem mounted. The config struct must
|
|
||||||
// be zeroed for defaults and backwards compatibility.
|
|
||||||
//
|
|
||||||
// Returns a negative error code on failure.
|
|
||||||
int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
* lfs util functions
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, The littlefs authors.
|
|
||||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
|
||||||
* SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
*/
|
|
||||||
#include "lfs_util.h"
|
|
||||||
|
|
||||||
// Only compile if user does not provide custom config
|
|
||||||
#ifndef LFS_CONFIG
|
|
||||||
|
|
||||||
|
|
||||||
// If user provides their own CRC impl we don't need this
|
|
||||||
#ifndef LFS_CRC
|
|
||||||
// Software CRC implementation with small lookup table
|
|
||||||
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
|
|
||||||
static const uint32_t rtable[16] = {
|
|
||||||
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
|
|
||||||
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
|
|
||||||
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
|
|
||||||
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
|
|
||||||
};
|
|
||||||
|
|
||||||
const uint8_t *data = buffer;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
|
||||||
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf];
|
|
||||||
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf];
|
|
||||||
}
|
|
||||||
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,255 +0,0 @@
|
||||||
/*
|
|
||||||
* lfs utility functions
|
|
||||||
*
|
|
||||||
* Copyright (c) 2022, The littlefs authors.
|
|
||||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
|
||||||
* SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
*/
|
|
||||||
#ifndef LFS_UTIL_H
|
|
||||||
#define LFS_UTIL_H
|
|
||||||
|
|
||||||
// Users can override lfs_util.h with their own configuration by defining
|
|
||||||
// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h).
|
|
||||||
//
|
|
||||||
// If LFS_CONFIG is used, none of the default utils will be emitted and must be
|
|
||||||
// provided by the config file. To start, I would suggest copying lfs_util.h
|
|
||||||
// and modifying as needed.
|
|
||||||
#ifdef LFS_CONFIG
|
|
||||||
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
|
|
||||||
#define LFS_STRINGIZE2(x) #x
|
|
||||||
#include LFS_STRINGIZE(LFS_CONFIG)
|
|
||||||
#else
|
|
||||||
|
|
||||||
// System includes
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#ifndef LFS_NO_MALLOC
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
#ifndef LFS_NO_ASSERT
|
|
||||||
#include <assert.h>
|
|
||||||
#endif
|
|
||||||
#if !defined(LFS_NO_DEBUG) || \
|
|
||||||
!defined(LFS_NO_WARN) || \
|
|
||||||
!defined(LFS_NO_ERROR) || \
|
|
||||||
defined(LFS_YES_TRACE)
|
|
||||||
#include <stdio.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// Macros, may be replaced by system specific wrappers. Arguments to these
|
|
||||||
// macros must not have side-effects as the macros can be removed for a smaller
|
|
||||||
// code footprint
|
|
||||||
|
|
||||||
// Logging functions
|
|
||||||
#ifndef LFS_TRACE
|
|
||||||
#ifdef LFS_YES_TRACE
|
|
||||||
#define LFS_TRACE_(fmt, ...) \
|
|
||||||
printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
|
||||||
#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
|
|
||||||
#else
|
|
||||||
#define LFS_TRACE(...)
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LFS_DEBUG
|
|
||||||
#ifndef LFS_NO_DEBUG
|
|
||||||
#define LFS_DEBUG_(fmt, ...) \
|
|
||||||
printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
|
||||||
#define LFS_DEBUG(...) LFS_DEBUG_(__VA_ARGS__, "")
|
|
||||||
#else
|
|
||||||
#define LFS_DEBUG(...)
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LFS_WARN
|
|
||||||
#ifndef LFS_NO_WARN
|
|
||||||
#define LFS_WARN_(fmt, ...) \
|
|
||||||
printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
|
||||||
#define LFS_WARN(...) LFS_WARN_(__VA_ARGS__, "")
|
|
||||||
#else
|
|
||||||
#define LFS_WARN(...)
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LFS_ERROR
|
|
||||||
#ifndef LFS_NO_ERROR
|
|
||||||
#define LFS_ERROR_(fmt, ...) \
|
|
||||||
printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
|
||||||
#define LFS_ERROR(...) LFS_ERROR_(__VA_ARGS__, "")
|
|
||||||
#else
|
|
||||||
#define LFS_ERROR(...)
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Runtime assertions
|
|
||||||
#ifndef LFS_ASSERT
|
|
||||||
#ifndef LFS_NO_ASSERT
|
|
||||||
#define LFS_ASSERT(test) assert(test)
|
|
||||||
#else
|
|
||||||
#define LFS_ASSERT(test)
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// Builtin functions, these may be replaced by more efficient
|
|
||||||
// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more
|
|
||||||
// expensive basic C implementation for debugging purposes
|
|
||||||
|
|
||||||
// Min/max functions for unsigned 32-bit numbers
|
|
||||||
static inline uint32_t lfs_max(uint32_t a, uint32_t b) {
|
|
||||||
return (a > b) ? a : b;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
|
|
||||||
return (a < b) ? a : b;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Align to nearest multiple of a size
|
|
||||||
static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) {
|
|
||||||
return a - (a % alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) {
|
|
||||||
return lfs_aligndown(a + alignment-1, alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the smallest power of 2 greater than or equal to a
|
|
||||||
static inline uint32_t lfs_npw2(uint32_t a) {
|
|
||||||
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
|
|
||||||
return 32 - __builtin_clz(a-1);
|
|
||||||
#else
|
|
||||||
uint32_t r = 0;
|
|
||||||
uint32_t s;
|
|
||||||
a -= 1;
|
|
||||||
s = (a > 0xffff) << 4; a >>= s; r |= s;
|
|
||||||
s = (a > 0xff ) << 3; a >>= s; r |= s;
|
|
||||||
s = (a > 0xf ) << 2; a >>= s; r |= s;
|
|
||||||
s = (a > 0x3 ) << 1; a >>= s; r |= s;
|
|
||||||
return (r | (a >> 1)) + 1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count the number of trailing binary zeros in a
|
|
||||||
// lfs_ctz(0) may be undefined
|
|
||||||
static inline uint32_t lfs_ctz(uint32_t a) {
|
|
||||||
#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__)
|
|
||||||
return __builtin_ctz(a);
|
|
||||||
#else
|
|
||||||
return lfs_npw2((a & -a) + 1) - 1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count the number of binary ones in a
|
|
||||||
static inline uint32_t lfs_popc(uint32_t a) {
|
|
||||||
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
|
|
||||||
return __builtin_popcount(a);
|
|
||||||
#else
|
|
||||||
a = a - ((a >> 1) & 0x55555555);
|
|
||||||
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
|
|
||||||
return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the sequence comparison of a and b, this is the distance
|
|
||||||
// between a and b ignoring overflow
|
|
||||||
static inline int lfs_scmp(uint32_t a, uint32_t b) {
|
|
||||||
return (int)(unsigned)(a - b);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert between 32-bit little-endian and native order
|
|
||||||
static inline uint32_t lfs_fromle32(uint32_t a) {
|
|
||||||
#if (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
|
|
||||||
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
|
|
||||||
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
|
||||||
return a;
|
|
||||||
#elif !defined(LFS_NO_INTRINSICS) && ( \
|
|
||||||
(defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
|
|
||||||
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
|
|
||||||
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
|
|
||||||
return __builtin_bswap32(a);
|
|
||||||
#else
|
|
||||||
return (((uint8_t*)&a)[0] << 0) |
|
|
||||||
(((uint8_t*)&a)[1] << 8) |
|
|
||||||
(((uint8_t*)&a)[2] << 16) |
|
|
||||||
(((uint8_t*)&a)[3] << 24);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t lfs_tole32(uint32_t a) {
|
|
||||||
return lfs_fromle32(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert between 32-bit big-endian and native order
|
|
||||||
static inline uint32_t lfs_frombe32(uint32_t a) {
|
|
||||||
#if !defined(LFS_NO_INTRINSICS) && ( \
|
|
||||||
(defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
|
|
||||||
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
|
|
||||||
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
|
|
||||||
return __builtin_bswap32(a);
|
|
||||||
#elif (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
|
|
||||||
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
|
|
||||||
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
|
||||||
return a;
|
|
||||||
#else
|
|
||||||
return (((uint8_t*)&a)[0] << 24) |
|
|
||||||
(((uint8_t*)&a)[1] << 16) |
|
|
||||||
(((uint8_t*)&a)[2] << 8) |
|
|
||||||
(((uint8_t*)&a)[3] << 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t lfs_tobe32(uint32_t a) {
|
|
||||||
return lfs_frombe32(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate CRC-32 with polynomial = 0x04c11db7
|
|
||||||
#ifdef LFS_CRC
|
|
||||||
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
|
|
||||||
return LFS_CRC(crc, buffer, size)
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Allocate memory, only used if buffers are not provided to littlefs
|
|
||||||
//
|
|
||||||
// littlefs current has no alignment requirements, as it only allocates
|
|
||||||
// byte-level buffers.
|
|
||||||
static inline void *lfs_malloc(size_t size) {
|
|
||||||
#if defined(LFS_MALLOC)
|
|
||||||
return LFS_MALLOC(size);
|
|
||||||
#elif !defined(LFS_NO_MALLOC)
|
|
||||||
return malloc(size);
|
|
||||||
#else
|
|
||||||
(void)size;
|
|
||||||
return NULL;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deallocate memory, only used if buffers are not provided to littlefs
|
|
||||||
static inline void lfs_free(void *p) {
|
|
||||||
#if defined(LFS_FREE)
|
|
||||||
LFS_FREE(p);
|
|
||||||
#elif !defined(LFS_NO_MALLOC)
|
|
||||||
free(p);
|
|
||||||
#else
|
|
||||||
(void)p;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif
|
|
32
src/lib/mining_pool/braiins/brains_pool.cpp
Normal file
32
src/lib/mining_pool/braiins/brains_pool.cpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#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)};
|
||||||
|
}
|
||||||
|
|
33
src/lib/mining_pool/braiins/brains_pool.hpp
Normal file
33
src/lib/mining_pool/braiins/brains_pool.hpp
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
};
|
6
src/lib/mining_pool/gobrrr_pool/gobrrr_pool.cpp
Normal file
6
src/lib/mining_pool/gobrrr_pool/gobrrr_pool.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// src/noderunners/noderunners_pool.cpp
|
||||||
|
#include "gobrrr_pool.hpp"
|
||||||
|
|
||||||
|
std::string GoBrrrPool::getApiUrl() const {
|
||||||
|
return "https://pool.gobrrr.me/api/client/" + poolUser;
|
||||||
|
}
|
30
src/lib/mining_pool/gobrrr_pool/gobrrr_pool.hpp
Normal file
30
src/lib/mining_pool/gobrrr_pool/gobrrr_pool.hpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
};
|
11
src/lib/mining_pool/logo_data.hpp
Normal file
11
src/lib/mining_pool/logo_data.hpp
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct LogoData {
|
||||||
|
const uint8_t* data;
|
||||||
|
size_t width;
|
||||||
|
size_t height;
|
||||||
|
size_t size;
|
||||||
|
};
|
18
src/lib/mining_pool/mining_pool_interface.cpp
Normal file
18
src/lib/mining_pool/mining_pool_interface.cpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#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);
|
||||||
|
}
|
35
src/lib/mining_pool/mining_pool_interface.hpp
Normal file
35
src/lib/mining_pool/mining_pool_interface.hpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#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;
|
||||||
|
};
|
95
src/lib/mining_pool/mining_pool_stats_handler.cpp
Normal file
95
src/lib/mining_pool/mining_pool_stats_handler.cpp
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#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;
|
||||||
|
}
|
11
src/lib/mining_pool/mining_pool_stats_handler.hpp
Normal file
11
src/lib/mining_pool/mining_pool_stats_handler.hpp
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#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);
|
27
src/lib/mining_pool/noderunners/noderunners_pool.cpp
Normal file
27
src/lib/mining_pool/noderunners/noderunners_pool.cpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// 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
|
||||||
|
};
|
||||||
|
}
|
33
src/lib/mining_pool/noderunners/noderunners_pool.hpp
Normal file
33
src/lib/mining_pool/noderunners/noderunners_pool.hpp
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
};
|
18
src/lib/mining_pool/ocean/ocean_pool.cpp
Normal file
18
src/lib/mining_pool/ocean/ocean_pool.cpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#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
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
31
src/lib/mining_pool/ocean/ocean_pool.hpp
Normal file
31
src/lib/mining_pool/ocean/ocean_pool.hpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
};
|
134
src/lib/mining_pool/pool_factory.cpp
Normal file
134
src/lib/mining_pool/pool_factory.cpp
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
#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;
|
||||||
|
}
|
56
src/lib/mining_pool/pool_factory.hpp
Normal file
56
src/lib/mining_pool/pool_factory.hpp
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#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;
|
||||||
|
};
|
10
src/lib/mining_pool/pool_stats.hpp
Normal file
10
src/lib/mining_pool/pool_stats.hpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
struct PoolStats {
|
||||||
|
std::string hashrate;
|
||||||
|
std::optional<int64_t> dailyEarnings;
|
||||||
|
};
|
21
src/lib/mining_pool/public_pool/public_pool.cpp
Normal file
21
src/lib/mining_pool/public_pool/public_pool.cpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// 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
|
||||||
|
};
|
||||||
|
}
|
15
src/lib/mining_pool/public_pool/public_pool.hpp
Normal file
15
src/lib/mining_pool/public_pool/public_pool.hpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
#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;
|
||||||
|
};
|
6
src/lib/mining_pool/satoshi_radio/satoshi_radio_pool.cpp
Normal file
6
src/lib/mining_pool/satoshi_radio/satoshi_radio_pool.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// src/noderunners/noderunners_pool.cpp
|
||||||
|
#include "satoshi_radio_pool.hpp"
|
||||||
|
|
||||||
|
std::string SatoshiRadioPool::getApiUrl() const {
|
||||||
|
return "https://pool.satoshiradio.nl/api/v1/users/" + poolUser;
|
||||||
|
}
|
14
src/lib/mining_pool/satoshi_radio/satoshi_radio_pool.hpp
Normal file
14
src/lib/mining_pool/satoshi_radio/satoshi_radio_pool.hpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
#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
|
||||||
|
};
|
123
src/lib/mining_pool_stats_fetch.cpp
Normal file
123
src/lib/mining_pool_stats_fetch.cpp
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
#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,
|
||||||
|
tskIDLE_PRIORITY,
|
||||||
|
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;
|
||||||
|
}
|
19
src/lib/mining_pool_stats_fetch.hpp
Normal file
19
src/lib/mining_pool_stats_fetch.hpp
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#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();
|
267
src/lib/nostr_notify.cpp
Normal file
267
src/lib/nostr_notify.cpp
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
#include "nostr_notify.hpp"
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
transport = nostr::esp32::ESP32Platform::getTransport();
|
||||||
|
nostr::NostrPool *pool = new nostr::NostrPool(transport);
|
||||||
|
String relay = preferences.getString("nostrRelay");
|
||||||
|
String pubKey = preferences.getString("nostrPubKey");
|
||||||
|
pools.push_back(pool);
|
||||||
|
|
||||||
|
std::vector<std::map<NostrString, std::initializer_list<NostrString>>> filters;
|
||||||
|
|
||||||
|
if (zapNotify)
|
||||||
|
{
|
||||||
|
subscribeZaps(pool, relay, 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asDatasource)
|
||||||
|
{
|
||||||
|
String subId = pool->subscribeMany(
|
||||||
|
{relay},
|
||||||
|
{// First filter
|
||||||
|
{
|
||||||
|
{"kinds", {"1"}},
|
||||||
|
{"since", {String(getMinutesAgo(60))}},
|
||||||
|
{"authors", {pubKey}},
|
||||||
|
}},
|
||||||
|
handleNostrEventCallback,
|
||||||
|
onNostrSubscriptionClosed,
|
||||||
|
onNostrSubscriptionEose);
|
||||||
|
|
||||||
|
Serial.println("[ Nostr ] Subscribing to Nostr Data Feed");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<nostr::NostrRelay *> *relays = pool->getConnectedRelays();
|
||||||
|
for (nostr::NostrRelay *relay : *relays)
|
||||||
|
{
|
||||||
|
Serial.println("[ Nostr ] Registering to connection events of: " + relay->getUrl());
|
||||||
|
relay->getConnection()->addConnectionStatusListener([&](const nostr::ConnectionStatus &status)
|
||||||
|
{
|
||||||
|
String sstatus="UNKNOWN";
|
||||||
|
if(status==nostr::ConnectionStatus::CONNECTED){
|
||||||
|
nostrIsConnected = true;
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
Serial.println("[ Nostr ] Error: " + String(e.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nostrTask(void *pvParameters)
|
||||||
|
{
|
||||||
|
if(preferences.getBool("useNostr", DEFAULT_USE_NOSTR)) {
|
||||||
|
int blockFetch = getBlockFetch();
|
||||||
|
processNewBlock(blockFetch);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
for (nostr::NostrPool *pool : pools)
|
||||||
|
{
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupNostrTask()
|
||||||
|
{
|
||||||
|
xTaskCreate(nostrTask, "nostrTask", 16384, NULL, 10, &nostrTaskHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean nostrConnected()
|
||||||
|
{
|
||||||
|
return nostrIsConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onNostrSubscriptionClosed(const String &subId, const String &reason)
|
||||||
|
{
|
||||||
|
// This is the callback that will be called when the subscription is
|
||||||
|
// closed
|
||||||
|
Serial.println("[ Nostr ] Subscription closed: " + reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// Received events callback, we can access the event content with
|
||||||
|
// event->getContent() Here you should handle the event, for this
|
||||||
|
// test we will just serialize it and print to console
|
||||||
|
JsonDocument doc;
|
||||||
|
JsonArray arr = doc["data"].to<JsonArray>();
|
||||||
|
event->toSendableEvent(arr);
|
||||||
|
// Access the second element which is the object
|
||||||
|
JsonObject obj = arr[1].as<JsonObject>();
|
||||||
|
JsonArray tags = obj["tags"].as<JsonArray>();
|
||||||
|
|
||||||
|
// Flag to check if the tag was found
|
||||||
|
bool tagFound = false;
|
||||||
|
uint medianFee = 0;
|
||||||
|
String typeValue;
|
||||||
|
|
||||||
|
// Iterate over the tags array
|
||||||
|
for (JsonArray tag : tags)
|
||||||
|
{
|
||||||
|
// Check if the tag is an array with two elements
|
||||||
|
if (tag.size() == 2)
|
||||||
|
{
|
||||||
|
const char *key = tag[0];
|
||||||
|
const char *value = tag[1];
|
||||||
|
|
||||||
|
// Check if the key is "type" and the value is "priceUsd"
|
||||||
|
if (strcmp(key, "type") == 0 && (strcmp(value, "priceUsd") == 0 || strcmp(value, "blockHeight") == 0))
|
||||||
|
{
|
||||||
|
typeValue = value;
|
||||||
|
tagFound = true;
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "medianFee") == 0)
|
||||||
|
{
|
||||||
|
medianFee = tag[1].as<uint>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tagFound)
|
||||||
|
{
|
||||||
|
if (typeValue.equals("priceUsd"))
|
||||||
|
{
|
||||||
|
processNewPrice(obj["content"].as<uint>(), CURRENCY_USD);
|
||||||
|
}
|
||||||
|
else if (typeValue.equals("blockHeight"))
|
||||||
|
{
|
||||||
|
processNewBlock(obj["content"].as<uint>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (medianFee != 0)
|
||||||
|
{
|
||||||
|
processNewBlockFee(medianFee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
// test we will just serialize it and print to console
|
||||||
|
JsonDocument doc;
|
||||||
|
JsonArray arr = doc["data"].to<JsonArray>();
|
||||||
|
event->toSendableEvent(arr);
|
||||||
|
// Access the second element which is the object
|
||||||
|
JsonObject obj = arr[1].as<JsonObject>();
|
||||||
|
JsonArray tags = obj["tags"].as<JsonArray>();
|
||||||
|
|
||||||
|
// Iterate over the tags array
|
||||||
|
for (JsonArray tag : tags)
|
||||||
|
{
|
||||||
|
// Check if the tag is an array with two elements
|
||||||
|
if (tag.size() == 2)
|
||||||
|
{
|
||||||
|
const char *key = tag[0];
|
||||||
|
const char *value = tag[1];
|
||||||
|
|
||||||
|
if (strcmp(key, "bolt11") == 0)
|
||||||
|
{
|
||||||
|
Serial.print(F("Got a zap of "));
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
uint64_t timerPeriod = 0;
|
||||||
|
if (isTimerActive())
|
||||||
|
{
|
||||||
|
// store timer periode before making inactive to prevent artifacts
|
||||||
|
timerPeriod = getTimerSeconds();
|
||||||
|
esp_timer_stop(screenRotateTimer);
|
||||||
|
}
|
||||||
|
setCurrentScreen(SCREEN_CUSTOM);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if (timerPeriod > 0)
|
||||||
|
{
|
||||||
|
esp_timer_start_periodic(screenRotateTimer,
|
||||||
|
timerPeriod * usPerSecond);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/lib/nostr_notify.hpp
Normal file
30
src/lib/nostr_notify.hpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "shared.hpp"
|
||||||
|
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <nostrdisplay_handler.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "esp32/ESP32Platform.h"
|
||||||
|
#include "NostrEvent.h"
|
||||||
|
#include "NostrPool.h"
|
||||||
|
|
||||||
|
#include "price_notify.hpp"
|
||||||
|
#include "block_notify.hpp"
|
||||||
|
#include "lib/timers.hpp"
|
||||||
|
|
||||||
|
void setupNostrNotify(bool asDatasource, bool zapNotify);
|
||||||
|
|
||||||
|
void nostrTask(void *pvParameters);
|
||||||
|
void setupNostrTask();
|
||||||
|
|
||||||
|
boolean nostrConnected();
|
||||||
|
void handleNostrEventCallback(const String &subId, nostr::SignedNostrEvent *event);
|
||||||
|
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);
|
421
src/lib/ota.cpp
421
src/lib/ota.cpp
|
@ -1,9 +1,15 @@
|
||||||
#include "ota.hpp"
|
#include "ota.hpp"
|
||||||
|
|
||||||
TaskHandle_t taskOtaHandle = NULL;
|
TaskHandle_t taskOtaHandle = NULL;
|
||||||
|
bool isOtaUpdating = false;
|
||||||
|
QueueHandle_t otaQueue;
|
||||||
|
|
||||||
void setupOTA() {
|
|
||||||
if (preferences.getBool("otaEnabled", true)) {
|
|
||||||
|
void setupOTA()
|
||||||
|
{
|
||||||
|
if (preferences.getBool("otaEnabled", DEFAULT_OTA_ENABLED))
|
||||||
|
{
|
||||||
ArduinoOTA.onStart(onOTAStart);
|
ArduinoOTA.onStart(onOTAStart);
|
||||||
|
|
||||||
ArduinoOTA.onProgress(onOTAProgress);
|
ArduinoOTA.onProgress(onOTAProgress);
|
||||||
|
@ -15,31 +21,38 @@ void setupOTA() {
|
||||||
ArduinoOTA.setRebootOnSuccess(false);
|
ArduinoOTA.setRebootOnSuccess(false);
|
||||||
ArduinoOTA.begin();
|
ArduinoOTA.begin();
|
||||||
// downloadUpdate();
|
// downloadUpdate();
|
||||||
|
otaQueue = xQueueCreate(1, sizeof(UpdateMessage));
|
||||||
|
|
||||||
xTaskCreate(handleOTATask, "handleOTA", 4096, NULL, tskIDLE_PRIORITY,
|
xTaskCreate(handleOTATask, "handleOTA", 8192, NULL, 20,
|
||||||
&taskOtaHandle);
|
&taskOtaHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onOTAProgress(unsigned int progress, unsigned int total) {
|
void onOTAProgress(unsigned int progress, unsigned int total)
|
||||||
|
{
|
||||||
uint percentage = progress / (total / 100);
|
uint percentage = progress / (total / 100);
|
||||||
pixels.fill(pixels.Color(0, 255, 0));
|
pixels.fill(pixels.Color(0, 255, 0));
|
||||||
if (percentage < 100) {
|
if (percentage < 100)
|
||||||
|
{
|
||||||
pixels.setPixelColor(0, pixels.Color(0, 0, 0));
|
pixels.setPixelColor(0, pixels.Color(0, 0, 0));
|
||||||
}
|
}
|
||||||
if (percentage < 75) {
|
if (percentage < 75)
|
||||||
|
{
|
||||||
pixels.setPixelColor(1, pixels.Color(0, 0, 0));
|
pixels.setPixelColor(1, pixels.Color(0, 0, 0));
|
||||||
}
|
}
|
||||||
if (percentage < 50) {
|
if (percentage < 50)
|
||||||
|
{
|
||||||
pixels.setPixelColor(2, pixels.Color(0, 0, 0));
|
pixels.setPixelColor(2, pixels.Color(0, 0, 0));
|
||||||
}
|
}
|
||||||
if (percentage < 25) {
|
if (percentage < 25)
|
||||||
|
{
|
||||||
pixels.setPixelColor(3, pixels.Color(0, 0, 0));
|
pixels.setPixelColor(3, pixels.Color(0, 0, 0));
|
||||||
}
|
}
|
||||||
pixels.show();
|
pixels.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onOTAStart() {
|
void onOTAStart()
|
||||||
|
{
|
||||||
forceFullRefresh();
|
forceFullRefresh();
|
||||||
std::array<String, NUM_SCREENS> epdContent = {"U", "P", "D", "A",
|
std::array<String, NUM_SCREENS> epdContent = {"U", "P", "D", "A",
|
||||||
"T", "E", "!"};
|
"T", "E", "!"};
|
||||||
|
@ -47,97 +60,385 @@ void onOTAStart() {
|
||||||
// Stop all timers
|
// Stop all timers
|
||||||
esp_timer_stop(screenRotateTimer);
|
esp_timer_stop(screenRotateTimer);
|
||||||
esp_timer_stop(minuteTimer);
|
esp_timer_stop(minuteTimer);
|
||||||
|
isOtaUpdating = true;
|
||||||
// Stop or suspend all tasks
|
// Stop or suspend all tasks
|
||||||
// vTaskSuspend(priceUpdateTaskHandle);
|
// vTaskSuspend(priceUpdateTaskHandle);
|
||||||
// vTaskSuspend(blockUpdateTaskHandle);
|
// vTaskSuspend(blockUpdateTaskHandle);
|
||||||
vTaskSuspend(workerTaskHandle);
|
vTaskSuspend(workerTaskHandle);
|
||||||
vTaskSuspend(taskScreenRotateTaskHandle);
|
vTaskSuspend(taskScreenRotateTaskHandle);
|
||||||
|
|
||||||
vTaskSuspend(ledTaskHandle);
|
// vTaskSuspend(ledTaskHandle);
|
||||||
vTaskSuspend(buttonTaskHandle);
|
vTaskSuspend(buttonTaskHandle);
|
||||||
|
|
||||||
stopWebServer();
|
// stopWebServer();
|
||||||
stopBlockNotify();
|
stopBlockNotify();
|
||||||
stopPriceNotify();
|
stopPriceNotify();
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleOTATask(void *parameter) {
|
void handleOTATask(void *parameter)
|
||||||
for (;;) {
|
{
|
||||||
ArduinoOTA.handle(); // Allow OTA updates to occur
|
UpdateMessage msg;
|
||||||
vTaskDelay(pdMS_TO_TICKS(2500));
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void downloadUpdate() {
|
ArduinoOTA.handle(); // Allow OTA updates to occur
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseInfo getLatestRelease(const String &fileToDownload)
|
||||||
|
{
|
||||||
|
String releaseUrl = preferences.getString("gitReleaseUrl");
|
||||||
WiFiClientSecure client;
|
WiFiClientSecure client;
|
||||||
client.setInsecure();
|
// client.setCACert(isrg_root_x1cert);
|
||||||
|
client.setCACertBundle(rootca_crt_bundle_start);
|
||||||
|
|
||||||
|
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
|
http.begin(client, releaseUrl);
|
||||||
http.setUserAgent(USER_AGENT);
|
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();
|
int httpCode = http.GET();
|
||||||
|
|
||||||
if (httpCode == 200) {
|
ReleaseInfo info = {"", ""};
|
||||||
// WiFiClient * stream = http->getStreamPtr();
|
|
||||||
|
|
||||||
JsonDocument filter;
|
if (httpCode > 0)
|
||||||
|
{
|
||||||
JsonObject filter_assets_0 = filter["assets"].add<JsonObject>();
|
String payload = http.getString();
|
||||||
filter_assets_0["name"] = true;
|
|
||||||
filter_assets_0["browser_download_url"] = true;
|
|
||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
|
deserializeJson(doc, payload);
|
||||||
|
|
||||||
DeserializationError error = deserializeJson(
|
JsonArray assets = doc["assets"];
|
||||||
doc, http.getStream(), DeserializationOption::Filter(filter));
|
|
||||||
|
|
||||||
if (error) {
|
for (JsonObject asset : assets)
|
||||||
Serial.print("deserializeJson() failed: ");
|
{
|
||||||
Serial.println(error.c_str());
|
String assetName = asset["name"].as<String>();
|
||||||
return;
|
if (assetName == fileToDownload)
|
||||||
|
{
|
||||||
|
info.fileUrl = asset["browser_download_url"].as<String>();
|
||||||
|
}
|
||||||
|
else if (assetName == fileToDownload + ".sha256")
|
||||||
|
{
|
||||||
|
info.checksumUrl = asset["browser_download_url"].as<String>();
|
||||||
}
|
}
|
||||||
|
|
||||||
String downloadUrl;
|
if (!info.fileUrl.isEmpty() && !info.checksumUrl.isEmpty())
|
||||||
for (JsonObject asset : doc["assets"].as<JsonArray>()) {
|
{
|
||||||
if (asset["name"].as<String>().compareTo("firmware.bin") == 0) {
|
|
||||||
downloadUrl = asset["browser_download_url"].as<String>();
|
|
||||||
break;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
Serial.printf("Download update from %s", downloadUrl);
|
int downloadUpdateHandler(char updateType)
|
||||||
|
{
|
||||||
|
WiFiClientSecure client;
|
||||||
|
client.setCACertBundle(rootca_crt_bundle_start);
|
||||||
|
HTTPClient http;
|
||||||
|
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||||
|
|
||||||
// esp_http_client_config_t config = {
|
ReleaseInfo latestRelease;
|
||||||
// .url = CONFIG_FIRMWARE_UPGRADE_URL,
|
|
||||||
// };
|
switch (updateType)
|
||||||
// esp_https_ota_config_t ota_config = {
|
{
|
||||||
// .http_config = &config,
|
case UPDATE_FIRMWARE:
|
||||||
// };
|
{
|
||||||
// esp_err_t ret = esp_https_ota(&ota_config);
|
latestRelease = getLatestRelease(getFirmwareFilename());
|
||||||
// if (ret == ESP_OK)
|
}
|
||||||
// {
|
break;
|
||||||
// esp_restart();
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onOTAError(ota_error_t error) {
|
void onOTAError(ota_error_t error)
|
||||||
Serial.println("\nOTA update error, restarting");
|
{
|
||||||
|
Serial.println(F("\nOTA update error, restarting"));
|
||||||
|
Wire.end();
|
||||||
|
SPI.end();
|
||||||
|
isOtaUpdating = false;
|
||||||
|
delay(1000);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onOTAComplete()
|
||||||
|
{
|
||||||
|
Serial.println(F("\nOTA update finished"));
|
||||||
Wire.end();
|
Wire.end();
|
||||||
SPI.end();
|
SPI.end();
|
||||||
delay(1000);
|
delay(1000);
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onOTAComplete() {
|
bool getIsOTAUpdating()
|
||||||
Serial.println("\nOTA update finished");
|
{
|
||||||
Wire.end();
|
return isOtaUpdating;
|
||||||
SPI.end();
|
}
|
||||||
delay(1000);
|
|
||||||
ESP.restart();
|
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,13 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <ArduinoOTA.h>
|
#include <ArduinoOTA.h>
|
||||||
|
|
||||||
#include "lib/config.hpp"
|
#include "lib/config.hpp"
|
||||||
#include "lib/shared.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 setupOTA();
|
||||||
void onOTAStart();
|
void onOTAStart();
|
||||||
void handleOTATask(void *parameter);
|
void handleOTATask(void *parameter);
|
||||||
void onOTAProgress(unsigned int progress, unsigned int total);
|
void onOTAProgress(unsigned int progress, unsigned int total);
|
||||||
void downloadUpdate();
|
// void downloadUpdate();
|
||||||
void onOTAError(ota_error_t error);
|
void onOTAError(ota_error_t error);
|
||||||
void onOTAComplete();
|
void onOTAComplete();
|
||||||
|
int downloadUpdateHandler(char updateType);
|
||||||
|
ReleaseInfo getLatestRelease(const String& fileToDownload);
|
||||||
|
|
||||||
|
bool getIsOTAUpdating();
|
||||||
|
|
||||||
|
void updateWebUi(String latestRelease, int command);
|
||||||
|
String downloadSHA256(const String& filename);
|
||||||
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
#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);
|
|
||||||
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER ||
|
|
||||||
getCurrentScreen() == SCREEN_MSCW_TIME ||
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
#include <Arduino.h>
|
|
||||||
#include <HTTPClient.h>
|
|
||||||
|
|
||||||
#include "lib/config.hpp"
|
|
||||||
#include "lib/shared.hpp"
|
|
||||||
|
|
||||||
extern TaskHandle_t priceFetchTaskHandle;
|
|
||||||
|
|
||||||
void setupPriceFetchTask();
|
|
||||||
void taskPriceFetch(void *pvParameters);
|
|
|
@ -1,70 +1,95 @@
|
||||||
#include "price_notify.hpp"
|
#include "price_notify.hpp"
|
||||||
|
|
||||||
|
const char *wsOwnServerPrice = "wss://ws.btclock.dev/ws?assets=bitcoin";
|
||||||
|
const char *wsOwnServerV2 = "wss://ws-staging.btclock.dev/api/v2/ws";
|
||||||
|
|
||||||
const char *wsServerPrice = "wss://ws.coincap.io/prices?assets=bitcoin";
|
const char *wsServerPrice = "wss://ws.coincap.io/prices?assets=bitcoin";
|
||||||
|
|
||||||
// const char* coinCapWsCert = R"(-----BEGIN CERTIFICATE-----
|
|
||||||
// MIIFMjCCBNmgAwIBAgIQBtgXvFyc28MsvQ1HjCnXJTAKBggqhkjOPQQDAjBKMQsw
|
|
||||||
// CQYDVQQGEwJVUzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEgMB4GA1UEAxMX
|
|
||||||
// Q2xvdWRmbGFyZSBJbmMgRUNDIENBLTMwHhcNMjMwNTEwMDAwMDAwWhcNMjQwNTA5
|
|
||||||
// MjM1OTU5WjB1MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQG
|
|
||||||
// A1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEe
|
|
||||||
// MBwGA1UEAxMVc25pLmNsb3VkZmxhcmVzc2wuY29tMFkwEwYHKoZIzj0CAQYIKoZI
|
|
||||||
// zj0DAQcDQgAEpvFIXzQKHuqTo+IE6c6sB4p0PMXK1KsseEGf2UN/CNRhG5hO7lr8
|
|
||||||
// JtXrPZkawWBysZxOsEoetkPrDHMugCLfXKOCA3QwggNwMB8GA1UdIwQYMBaAFKXO
|
|
||||||
// N+rrsHUOlGeItEX62SQQh5YfMB0GA1UdDgQWBBShsZDJohaR1a5E0Qj7yblZjKDC
|
|
||||||
// gDA6BgNVHREEMzAxggwqLmNvaW5jYXAuaW+CCmNvaW5jYXAuaW+CFXNuaS5jbG91
|
|
||||||
// ZGZsYXJlc3NsLmNvbTAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUH
|
|
||||||
// AwEGCCsGAQUFBwMCMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2lj
|
|
||||||
// ZXJ0LmNvbS9DbG91ZGZsYXJlSW5jRUNDQ0EtMy5jcmwwN6A1oDOGMWh0dHA6Ly9j
|
|
||||||
// cmw0LmRpZ2ljZXJ0LmNvbS9DbG91ZGZsYXJlSW5jRUNDQ0EtMy5jcmwwPgYDVR0g
|
|
||||||
// BDcwNTAzBgZngQwBAgIwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2Vy
|
|
||||||
// dC5jb20vQ1BTMHYGCCsGAQUFBwEBBGowaDAkBggrBgEFBQcwAYYYaHR0cDovL29j
|
|
||||||
// c3AuZGlnaWNlcnQuY29tMEAGCCsGAQUFBzAChjRodHRwOi8vY2FjZXJ0cy5kaWdp
|
|
||||||
// Y2VydC5jb20vQ2xvdWRmbGFyZUluY0VDQ0NBLTMuY3J0MAwGA1UdEwEB/wQCMAAw
|
|
||||||
// ggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB1AO7N0GTV2xrOxVy3nbTNE6Iyh0Z8
|
|
||||||
// vOzew1FIWUZxH7WbAAABiAPnoRAAAAQDAEYwRAIgAP2W09OozuhmKeKKMsaVBcae
|
|
||||||
// o+nPHF1WUWk0i387YYYCIDIM1Wll7/4O3GNx2/Fx9bC6pi69Uya4pLxsCfW3fZMe
|
|
||||||
// AHYASLDja9qmRzQP5WoC+p0w6xxSActW3SyB2bu/qznYhHMAAAGIA+eg+QAABAMA
|
|
||||||
// RzBFAiEAuNpSqrbx47gYBgBMz5M6q0CnV/WMJqWQOxYFKrwfwVACIH3nCs4bKToT
|
|
||||||
// e+MiBrqSDaekixk4kPFEQESO9qHCkWY5AHcA2ra/az+1tiKfm8K7XGvocJFxbLtR
|
|
||||||
// hIU0vaQ9MEjX+6sAAAGIA+eg1gAABAMASDBGAiEAolCFl2IfbOHUPAOxoi4BLclS
|
|
||||||
// v9FVXb7LwIvTuCfyrEQCIQDcvehwhV9XGopKGl17F2LYYKI7hvlO3RmpPZQJt1da
|
|
||||||
// MDAKBggqhkjOPQQDAgNHADBEAiAXRWZ/JVMsfpSFFTHQHUSqRnQ/7cCOWx+9svIy
|
|
||||||
// mYnFZQIgHMEG0Cm7O4cn5KUzKOsTwwK+2U15s/jPUQi2n2IDTEM=
|
|
||||||
// -----END CERTIFICATE-----)";
|
|
||||||
|
|
||||||
// WebsocketsClient client;
|
// WebsocketsClient client;
|
||||||
esp_websocket_client_handle_t clientPrice = NULL;
|
esp_websocket_client_handle_t clientPrice = NULL;
|
||||||
uint currentPrice = 30000;
|
esp_websocket_client_config_t config;
|
||||||
|
uint currentPrice = 90000;
|
||||||
unsigned long int lastPriceUpdate;
|
unsigned long int lastPriceUpdate;
|
||||||
bool priceNotifyInit = false;
|
bool priceNotifyInit = false;
|
||||||
|
std::map<char, std::uint64_t> currencyMap;
|
||||||
|
std::map<char, unsigned long int> lastUpdateMap;
|
||||||
|
WebSocketsClient priceNotifyWs;
|
||||||
|
|
||||||
void setupPriceNotify() {
|
void setupPriceNotify()
|
||||||
// currentPrice = preferences.get("lastPrice", 30000);
|
{
|
||||||
|
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE))
|
||||||
esp_websocket_client_config_t config = {.uri = wsServerPrice,
|
{
|
||||||
// .task_stack = (7*1024),
|
config = {.uri = wsOwnServerPrice,
|
||||||
// .cert_pem = coinCapWsCert,
|
|
||||||
.user_agent = USER_AGENT};
|
.user_agent = USER_AGENT};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
config = {.uri = wsServerPrice,
|
||||||
|
.user_agent = USER_AGENT};
|
||||||
|
config.cert_pem = isrg_root_x1cert;
|
||||||
|
|
||||||
|
config.task_stack = (6*1024);
|
||||||
|
}
|
||||||
|
|
||||||
clientPrice = esp_websocket_client_init(&config);
|
clientPrice = esp_websocket_client_init(&config);
|
||||||
esp_websocket_register_events(clientPrice, WEBSOCKET_EVENT_ANY,
|
esp_websocket_register_events(clientPrice, WEBSOCKET_EVENT_ANY,
|
||||||
onWebsocketPriceEvent, clientPrice);
|
onWebsocketPriceEvent, clientPrice);
|
||||||
esp_websocket_client_start(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,
|
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
|
||||||
int32_t event_id, void *event_data) {
|
int32_t event_id, void *event_data)
|
||||||
|
{
|
||||||
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
|
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
|
||||||
|
|
||||||
switch (event_id) {
|
switch (event_id)
|
||||||
|
{
|
||||||
case WEBSOCKET_EVENT_CONNECTED:
|
case WEBSOCKET_EVENT_CONNECTED:
|
||||||
Serial.println(F("Connected to CoinCap.io WebSocket"));
|
Serial.println("Connected to " + String(config.uri) + " WebSocket");
|
||||||
priceNotifyInit = true;
|
priceNotifyInit = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case WEBSOCKET_EVENT_DATA:
|
case WEBSOCKET_EVENT_DATA:
|
||||||
onWebsocketPriceMessage(data);
|
onWebsocketPriceMessage(data);
|
||||||
|
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE))
|
||||||
|
{
|
||||||
|
onWebsocketBlockMessage(data);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case WEBSOCKET_EVENT_ERROR:
|
case WEBSOCKET_EVENT_ERROR:
|
||||||
Serial.println(F("Price WS Connnection error"));
|
Serial.println(F("Price WS Connnection error"));
|
||||||
|
@ -75,58 +100,106 @@ void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data) {
|
void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data)
|
||||||
|
{
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
|
|
||||||
deserializeJson(doc, (char *)event_data->data_ptr);
|
deserializeJson(doc, (char *)event_data->data_ptr);
|
||||||
|
|
||||||
if (doc.containsKey("bitcoin")) {
|
if (doc.containsKey("bitcoin"))
|
||||||
if (currentPrice != doc["bitcoin"].as<long>()) {
|
{
|
||||||
|
if (currentPrice != doc["bitcoin"].as<long>())
|
||||||
|
{
|
||||||
|
processNewPrice(doc["bitcoin"].as<long>(), CURRENCY_USD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void processNewPrice(uint newPrice, char currency)
|
||||||
|
{
|
||||||
uint minSecPriceUpd = preferences.getUInt(
|
uint minSecPriceUpd = preferences.getUInt(
|
||||||
"minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE);
|
"minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE);
|
||||||
uint currentTime = esp_timer_get_time() / 1000000;
|
uint currentTime = esp_timer_get_time() / 1000000;
|
||||||
|
|
||||||
if (lastPriceUpdate == 0 ||
|
if (lastUpdateMap.find(currency) == lastUpdateMap.end() ||
|
||||||
(currentTime - lastPriceUpdate) > minSecPriceUpd) {
|
(currentTime - lastUpdateMap[currency]) > minSecPriceUpd)
|
||||||
|
{
|
||||||
// const unsigned long oldPrice = currentPrice;
|
// const unsigned long oldPrice = currentPrice;
|
||||||
currentPrice = doc["bitcoin"].as<uint>();
|
currencyMap[currency] = newPrice;
|
||||||
|
if (currency == CURRENCY_USD && ( lastUpdateMap[currency] == 0 ||
|
||||||
|
(currentTime - lastUpdateMap[currency]) > 120))
|
||||||
|
{
|
||||||
preferences.putUInt("lastPrice", currentPrice);
|
preferences.putUInt("lastPrice", currentPrice);
|
||||||
lastPriceUpdate = currentTime;
|
}
|
||||||
|
lastUpdateMap[currency] = currentTime;
|
||||||
// if (abs((int)(oldPrice-currentPrice)) > round(0.0015*oldPrice)) {
|
// if (abs((int)(oldPrice-currentPrice)) > round(0.0015*oldPrice)) {
|
||||||
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER ||
|
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER ||
|
||||||
getCurrentScreen() == SCREEN_MSCW_TIME ||
|
getCurrentScreen() == SCREEN_SATS_PER_CURRENCY ||
|
||||||
getCurrentScreen() == SCREEN_MARKET_CAP)) {
|
getCurrentScreen() == SCREEN_MARKET_CAP))
|
||||||
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
|
{
|
||||||
|
WorkItem priceUpdate = {TASK_PRICE_UPDATE, currency};
|
||||||
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
||||||
}
|
}
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
uint getLastPriceUpdate(char currency)
|
||||||
|
{
|
||||||
|
if (lastUpdateMap.find(currency) == lastUpdateMap.end())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint getLastPriceUpdate() {
|
return lastUpdateMap[currency];
|
||||||
return lastPriceUpdate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint getPrice() { return currentPrice; }
|
uint getPrice(char currency)
|
||||||
|
{
|
||||||
|
if (currencyMap.find(currency) == currencyMap.end())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return currencyMap[currency];
|
||||||
|
}
|
||||||
|
|
||||||
void setPrice(uint newPrice) { currentPrice = newPrice; }
|
void setPrice(uint newPrice, char currency)
|
||||||
|
{
|
||||||
|
currencyMap[currency] = newPrice;
|
||||||
|
}
|
||||||
|
|
||||||
bool isPriceNotifyConnected() {
|
bool isPriceNotifyConnected()
|
||||||
if (clientPrice == NULL) return false;
|
{
|
||||||
|
if (clientPrice == NULL)
|
||||||
|
return false;
|
||||||
return esp_websocket_client_is_connected(clientPrice);
|
return esp_websocket_client_is_connected(clientPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getPriceNotifyInit() {
|
bool getPriceNotifyInit()
|
||||||
|
{
|
||||||
return priceNotifyInit;
|
return priceNotifyInit;
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopPriceNotify() {
|
void stopPriceNotify()
|
||||||
if (clientPrice == NULL) return;
|
{
|
||||||
esp_websocket_client_close(clientPrice, portMAX_DELAY);
|
if (clientPrice == NULL)
|
||||||
|
return;
|
||||||
|
esp_websocket_client_close(clientPrice, pdMS_TO_TICKS(5000));
|
||||||
esp_websocket_client_stop(clientPrice);
|
esp_websocket_client_stop(clientPrice);
|
||||||
esp_websocket_client_destroy(clientPrice);
|
esp_websocket_client_destroy(clientPrice);
|
||||||
|
|
||||||
clientPrice = NULL;
|
clientPrice = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void restartPriceNotify()
|
||||||
|
{
|
||||||
|
stopPriceNotify();
|
||||||
|
if (clientPrice == NULL)
|
||||||
|
{
|
||||||
|
setupPriceNotify();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// esp_websocket_client_close(clientPrice, pdMS_TO_TICKS(5000));
|
||||||
|
// esp_websocket_client_stop(clientPrice);
|
||||||
|
// esp_websocket_client_start(clientPrice);
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <esp_websocket_client.h>
|
#include <esp_websocket_client.h>
|
||||||
|
#include "block_notify.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "lib/screen_handler.hpp"
|
#include "lib/screen_handler.hpp"
|
||||||
|
@ -12,12 +12,19 @@ void setupPriceNotify();
|
||||||
|
|
||||||
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
|
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
|
||||||
int32_t event_id, void *event_data);
|
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);
|
void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data);
|
||||||
|
|
||||||
uint getPrice();
|
uint getPrice(char currency);
|
||||||
void setPrice(uint newPrice);
|
void setPrice(uint newPrice, char currency);
|
||||||
|
|
||||||
|
//void processNewPrice(uint newPrice);
|
||||||
|
void processNewPrice(uint newPrice, char currency);
|
||||||
|
|
||||||
bool isPriceNotifyConnected();
|
bool isPriceNotifyConnected();
|
||||||
void stopPriceNotify();
|
void stopPriceNotify();
|
||||||
|
void restartPriceNotify();
|
||||||
|
|
||||||
bool getPriceNotifyInit();
|
bool getPriceNotifyInit();
|
||||||
uint getLastPriceUpdate();
|
uint getLastPriceUpdate(char currency);
|
|
@ -5,17 +5,16 @@
|
||||||
// TaskHandle_t timeUpdateTaskHandle;
|
// TaskHandle_t timeUpdateTaskHandle;
|
||||||
TaskHandle_t taskScreenRotateTaskHandle;
|
TaskHandle_t taskScreenRotateTaskHandle;
|
||||||
TaskHandle_t workerTaskHandle;
|
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;
|
std::string priceString;
|
||||||
|
|
||||||
#define WORK_QUEUE_SIZE 10
|
#define WORK_QUEUE_SIZE 10
|
||||||
QueueHandle_t workQueue = NULL;
|
QueueHandle_t workQueue = NULL;
|
||||||
|
|
||||||
uint currentScreen;
|
uint currentScreen = SCREEN_BLOCK_HEIGHT;
|
||||||
|
uint currentCurrency = CURRENCY_USD;
|
||||||
|
|
||||||
void workerTask(void *pvParameters) {
|
void workerTask(void *pvParameters) {
|
||||||
WorkItem receivedItem;
|
WorkItem receivedItem;
|
||||||
|
@ -23,24 +22,45 @@ void workerTask(void *pvParameters) {
|
||||||
while (1) {
|
while (1) {
|
||||||
// Wait for a work item to be available in the queue
|
// Wait for a work item to be available in the queue
|
||||||
if (xQueueReceive(workQueue, &receivedItem, portMAX_DELAY)) {
|
if (xQueueReceive(workQueue, &receivedItem, portMAX_DELAY)) {
|
||||||
uint firstIndex = 0;
|
|
||||||
|
|
||||||
// Process the work item based on its type
|
// Process the work item based on its type
|
||||||
switch (receivedItem.type) {
|
switch (receivedItem.type) {
|
||||||
case TASK_PRICE_UPDATE: {
|
case TASK_BITAXE_UPDATE: {
|
||||||
uint price = getPrice();
|
if (getCurrentScreen() == SCREEN_BITAXE_HASHRATE) {
|
||||||
char priceSymbol = '$';
|
taskEpdContent =
|
||||||
if (preferences.getBool("fetchEurPrice", false)) {
|
parseBitaxeHashRate(getBitAxeHashRate());
|
||||||
priceSymbol = '[';
|
} else if (getCurrentScreen() == SCREEN_BITAXE_BESTDIFF) {
|
||||||
|
taskEpdContent =
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
case TASK_PRICE_UPDATE: {
|
||||||
|
uint currency = getCurrentCurrency();
|
||||||
|
uint price = getPrice(currency);
|
||||||
|
|
||||||
if (getCurrentScreen() == SCREEN_BTC_TICKER) {
|
if (getCurrentScreen() == SCREEN_BTC_TICKER) {
|
||||||
taskEpdContent = parsePriceData(price, priceSymbol, preferences.getBool("suffixPrice", false));
|
taskEpdContent = parsePriceData(price, currency, preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE),
|
||||||
} else if (getCurrentScreen() == SCREEN_MSCW_TIME) {
|
preferences.getBool("mowMode", DEFAULT_MOW_MODE),
|
||||||
taskEpdContent = parseSatsPerCurrency(price, priceSymbol, preferences.getBool("useSatsSymbol", false));
|
preferences.getBool("suffixShareDot", DEFAULT_SUFFIX_SHARE_DOT)
|
||||||
|
);
|
||||||
|
} else if (getCurrentScreen() == SCREEN_SATS_PER_CURRENCY) {
|
||||||
|
taskEpdContent = parseSatsPerCurrency(price, currency, preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL));
|
||||||
} else {
|
} else {
|
||||||
taskEpdContent =
|
taskEpdContent =
|
||||||
parseMarketCap(getBlockHeight(), price, priceSymbol,
|
parseMarketCap(getBlockHeight(), price, currency,
|
||||||
preferences.getBool("mcapBigChar", true));
|
preferences.getBool("mcapBigChar", DEFAULT_MCAP_BIG_CHAR));
|
||||||
}
|
}
|
||||||
|
|
||||||
setEpdContent(taskEpdContent);
|
setEpdContent(taskEpdContent);
|
||||||
|
@ -57,7 +77,7 @@ void workerTask(void *pvParameters) {
|
||||||
if (getCurrentScreen() != SCREEN_HALVING_COUNTDOWN) {
|
if (getCurrentScreen() != SCREEN_HALVING_COUNTDOWN) {
|
||||||
taskEpdContent = parseBlockHeight(getBlockHeight());
|
taskEpdContent = parseBlockHeight(getBlockHeight());
|
||||||
} else {
|
} else {
|
||||||
taskEpdContent = parseHalvingCountdown(getBlockHeight(), preferences.getBool("useBlkCountdown", false));
|
taskEpdContent = parseHalvingCountdown(getBlockHeight(), preferences.getBool("useBlkCountdown", DEFAULT_USE_BLOCK_COUNTDOWN));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getCurrentScreen() == SCREEN_HALVING_COUNTDOWN ||
|
if (getCurrentScreen() == SCREEN_HALVING_COUNTDOWN ||
|
||||||
|
@ -104,36 +124,7 @@ void taskScreenRotate(void *pvParameters) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
|
|
||||||
int nextScreen = (currentScreen + 1) % SCREEN_COUNT;
|
nextScreen();
|
||||||
String key = "screen" + String(nextScreen) + "Visible";
|
|
||||||
|
|
||||||
while (!preferences.getBool(key.c_str(), true)) {
|
|
||||||
nextScreen = (nextScreen + 1) % SCREEN_COUNT;
|
|
||||||
key = "screen" + String(nextScreen) + "Visible";
|
|
||||||
}
|
|
||||||
|
|
||||||
setCurrentScreen(nextScreen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (xHigherPriorityTaskWoken == pdTRUE) {
|
|
||||||
portYIELD_FROM_ISR();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IRAM_ATTR screenRotateTimerISR(void *arg) {
|
|
||||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
|
||||||
vTaskNotifyGiveFromISR(taskScreenRotateTaskHandle, &xHigherPriorityTaskWoken);
|
|
||||||
if (xHigherPriorityTaskWoken == pdTRUE) {
|
|
||||||
portYIELD_FROM_ISR();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,72 +134,15 @@ void setupTasks() {
|
||||||
xTaskCreate(workerTask, "workerTask", 4096, NULL, tskIDLE_PRIORITY,
|
xTaskCreate(workerTask, "workerTask", 4096, NULL, tskIDLE_PRIORITY,
|
||||||
&workerTaskHandle);
|
&workerTaskHandle);
|
||||||
|
|
||||||
xTaskCreate(taskScreenRotate, "rotateScreen", 2048, NULL, tskIDLE_PRIORITY,
|
xTaskCreate(taskScreenRotate, "rotateScreen", 4096, NULL, tskIDLE_PRIORITY,
|
||||||
&taskScreenRotateTaskHandle);
|
&taskScreenRotateTaskHandle);
|
||||||
|
|
||||||
waitUntilNoneBusy();
|
waitUntilNoneBusy();
|
||||||
setCurrentScreen(preferences.getUInt("currentScreen", 0));
|
|
||||||
|
if (findScreenIndexByValue(preferences.getUInt("currentScreen", DEFAULT_CURRENT_SCREEN)) != -1)
|
||||||
|
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", true)) {
|
|
||||||
esp_timer_start_periodic(screenRotateTimer,
|
|
||||||
getTimerSeconds() * usPerSecond);
|
|
||||||
}
|
|
||||||
|
|
||||||
vTaskDelete(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint getTimerSeconds() { return preferences.getUInt("timerSeconds", 1800); }
|
|
||||||
|
|
||||||
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; }
|
uint getCurrentScreen() { return currentScreen; }
|
||||||
|
|
||||||
void setCurrentScreen(uint newScreen) {
|
void setCurrentScreen(uint newScreen) {
|
||||||
|
@ -233,7 +167,7 @@ void setCurrentScreen(uint newScreen) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SCREEN_MARKET_CAP:
|
case SCREEN_MARKET_CAP:
|
||||||
case SCREEN_MSCW_TIME:
|
case SCREEN_SATS_PER_CURRENCY:
|
||||||
case SCREEN_BTC_TICKER: {
|
case SCREEN_BTC_TICKER: {
|
||||||
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
|
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
|
||||||
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
||||||
|
@ -245,36 +179,135 @@ void setCurrentScreen(uint newScreen) {
|
||||||
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SCREEN_BITAXE_BESTDIFF:
|
||||||
|
case SCREEN_BITAXE_HASHRATE: {
|
||||||
|
if (preferences.getBool("bitaxeEnabled", DEFAULT_BITAXE_ENABLED)) {
|
||||||
|
WorkItem bitaxeUpdate = {TASK_BITAXE_UPDATE, 0};
|
||||||
|
xQueueSend(workQueue, &bitaxeUpdate, portMAX_DELAY);
|
||||||
|
} else {
|
||||||
|
setCurrentScreen(SCREEN_BLOCK_HEIGHT);
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventSourceTaskHandle != NULL) xTaskNotifyGive(eventSourceTaskHandle);
|
if (eventSourceTaskHandle != NULL) xTaskNotifyGive(eventSourceTaskHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isCurrencySpecific(uint screen) {
|
||||||
|
switch (screen) {
|
||||||
|
case SCREEN_BTC_TICKER:
|
||||||
|
case SCREEN_SATS_PER_CURRENCY:
|
||||||
|
case SCREEN_MARKET_CAP:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void nextScreen() {
|
void nextScreen() {
|
||||||
int newCurrentScreen = (getCurrentScreen() + 1) % SCREEN_COUNT;
|
int currentIndex = findScreenIndexByValue(getCurrentScreen());
|
||||||
|
std::vector<ScreenMapping> screenMappings = getScreenNameMap();
|
||||||
|
|
||||||
|
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE) && isCurrencySpecific(getCurrentScreen())) {
|
||||||
|
std::vector<std::string> ac = getActiveCurrencies();
|
||||||
|
std::string curCode = getCurrencyCode(getCurrentCurrency());
|
||||||
|
if (getCurrencyCode(getCurrentCurrency()) != ac.back()) {
|
||||||
|
auto it = std::find(ac.begin(), ac.end(), curCode);
|
||||||
|
if (it != ac.end()) {
|
||||||
|
size_t index = std::distance(ac.begin(), it);
|
||||||
|
setCurrentCurrency(getCurrencyChar(ac.at(index+1)));
|
||||||
|
setCurrentScreen(getCurrentScreen());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCurrentCurrency(getCurrencyChar(ac.front()));
|
||||||
|
}
|
||||||
|
|
||||||
|
int newCurrentScreen;
|
||||||
|
|
||||||
|
if (currentIndex < screenMappings.size() - 1) {
|
||||||
|
newCurrentScreen = (screenMappings[currentIndex + 1].value);
|
||||||
|
} else {
|
||||||
|
newCurrentScreen = screenMappings.front().value;
|
||||||
|
}
|
||||||
|
|
||||||
String key = "screen" + String(newCurrentScreen) + "Visible";
|
String key = "screen" + String(newCurrentScreen) + "Visible";
|
||||||
|
|
||||||
while (!preferences.getBool(key.c_str(), true)) {
|
while (!preferences.getBool(key.c_str(), true)) {
|
||||||
newCurrentScreen = (newCurrentScreen + 1) % SCREEN_COUNT;
|
currentIndex = findScreenIndexByValue(newCurrentScreen);
|
||||||
|
if (currentIndex < screenMappings.size() - 1) {
|
||||||
|
newCurrentScreen = (screenMappings[currentIndex + 1].value);
|
||||||
|
} else {
|
||||||
|
newCurrentScreen = screenMappings.front().value;
|
||||||
|
}
|
||||||
|
|
||||||
key = "screen" + String(newCurrentScreen) + "Visible";
|
key = "screen" + String(newCurrentScreen) + "Visible";
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentScreen(newCurrentScreen);
|
setCurrentScreen(newCurrentScreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void previousScreen() {
|
void previousScreen() {
|
||||||
int newCurrentScreen = modulo(getCurrentScreen() - 1, SCREEN_COUNT);
|
int currentIndex = findScreenIndexByValue(getCurrentScreen());
|
||||||
|
std::vector<ScreenMapping> screenMappings = getScreenNameMap();
|
||||||
|
|
||||||
|
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE) && isCurrencySpecific(getCurrentScreen())) {
|
||||||
|
std::vector<std::string> ac = getActiveCurrencies();
|
||||||
|
std::string curCode = getCurrencyCode(getCurrentCurrency());
|
||||||
|
if (getCurrencyCode(getCurrentCurrency()) != ac.front()) {
|
||||||
|
auto it = std::find(ac.begin(), ac.end(), curCode);
|
||||||
|
if (it != ac.end()) {
|
||||||
|
size_t index = std::distance(ac.begin(), it);
|
||||||
|
setCurrentCurrency(getCurrencyChar(ac.at(index-1)));
|
||||||
|
setCurrentScreen(getCurrentScreen());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCurrentCurrency(getCurrencyChar(ac.back()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int newCurrentScreen;
|
||||||
|
|
||||||
|
if (currentIndex > 0) {
|
||||||
|
newCurrentScreen = screenMappings[currentIndex - 1].value;
|
||||||
|
} else {
|
||||||
|
newCurrentScreen = screenMappings.back().value;
|
||||||
|
}
|
||||||
|
|
||||||
String key = "screen" + String(newCurrentScreen) + "Visible";
|
String key = "screen" + String(newCurrentScreen) + "Visible";
|
||||||
|
|
||||||
while (!preferences.getBool(key.c_str(), true)) {
|
while (!preferences.getBool(key.c_str(), true)) {
|
||||||
newCurrentScreen = modulo(newCurrentScreen - 1, SCREEN_COUNT);
|
int currentIndex = findScreenIndexByValue(newCurrentScreen);
|
||||||
|
if (currentIndex > 0) {
|
||||||
|
newCurrentScreen = screenMappings[currentIndex - 1].value;
|
||||||
|
} else {
|
||||||
|
newCurrentScreen = screenMappings.back().value;
|
||||||
|
}
|
||||||
|
|
||||||
key = "screen" + String(newCurrentScreen) + "Visible";
|
key = "screen" + String(newCurrentScreen) + "Visible";
|
||||||
}
|
}
|
||||||
setCurrentScreen(newCurrentScreen);
|
setCurrentScreen(newCurrentScreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showSystemStatusScreen() {
|
void showSystemStatusScreen() {
|
||||||
std::array<String, NUM_SCREENS> sysStatusEpdContent = {"", "", "", "",
|
std::array<String, NUM_SCREENS> sysStatusEpdContent;
|
||||||
"", "", ""};
|
std::fill(sysStatusEpdContent.begin(), sysStatusEpdContent.end(), "");
|
||||||
|
|
||||||
|
|
||||||
String ipAddr = WiFi.localIP().toString();
|
String ipAddr = WiFi.localIP().toString();
|
||||||
String subNet = WiFi.subnetMask().toString();
|
String subNet = WiFi.subnetMask().toString();
|
||||||
|
@ -299,3 +332,12 @@ void showSystemStatusScreen() {
|
||||||
setCurrentScreen(SCREEN_CUSTOM);
|
setCurrentScreen(SCREEN_CUSTOM);
|
||||||
setEpdContent(sysStatusEpdContent);
|
setEpdContent(sysStatusEpdContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setCurrentCurrency(char currency) {
|
||||||
|
currentCurrency = currency;
|
||||||
|
preferences.putUChar("lastCurrency", currency);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint getCurrentCurrency() {
|
||||||
|
return currentCurrency;
|
||||||
|
}
|
|
@ -5,9 +5,10 @@
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
|
|
||||||
#include <data_handler.hpp>
|
#include <data_handler.hpp>
|
||||||
|
#include <bitaxe_handler.hpp>
|
||||||
|
#include "lib/mining_pool/mining_pool_stats_handler.hpp"
|
||||||
|
|
||||||
#include "lib/epd.hpp"
|
#include "lib/epd.hpp"
|
||||||
#include "lib/price_fetch.hpp"
|
|
||||||
#include "lib/shared.hpp"
|
#include "lib/shared.hpp"
|
||||||
|
|
||||||
// extern TaskHandle_t priceUpdateTaskHandle;
|
// extern TaskHandle_t priceUpdateTaskHandle;
|
||||||
|
@ -16,16 +17,15 @@
|
||||||
extern TaskHandle_t workerTaskHandle;
|
extern TaskHandle_t workerTaskHandle;
|
||||||
extern TaskHandle_t taskScreenRotateTaskHandle;
|
extern TaskHandle_t taskScreenRotateTaskHandle;
|
||||||
|
|
||||||
extern esp_timer_handle_t screenRotateTimer;
|
|
||||||
extern esp_timer_handle_t minuteTimer;
|
|
||||||
|
|
||||||
extern QueueHandle_t workQueue;
|
extern QueueHandle_t workQueue;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TASK_PRICE_UPDATE,
|
TASK_PRICE_UPDATE,
|
||||||
TASK_BLOCK_UPDATE,
|
TASK_BLOCK_UPDATE,
|
||||||
TASK_FEE_UPDATE,
|
TASK_FEE_UPDATE,
|
||||||
TASK_TIME_UPDATE
|
TASK_TIME_UPDATE,
|
||||||
|
TASK_BITAXE_UPDATE,
|
||||||
|
TASK_MINING_POOL_STATS_UPDATE
|
||||||
} TaskType;
|
} TaskType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -41,20 +41,16 @@ void previousScreen();
|
||||||
|
|
||||||
void showSystemStatusScreen();
|
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 taskPriceUpdate(void *pvParameters);
|
||||||
// void taskBlockUpdate(void *pvParameters);
|
// void taskBlockUpdate(void *pvParameters);
|
||||||
// void taskTimeUpdate(void *pvParameters);
|
// void taskTimeUpdate(void *pvParameters);
|
||||||
void taskScreenRotate(void *pvParameters);
|
void taskScreenRotate(void *pvParameters);
|
||||||
|
|
||||||
uint getTimerSeconds();
|
|
||||||
bool isTimerActive();
|
|
||||||
void setTimerActive(bool status);
|
|
||||||
void toggleTimerActive();
|
|
||||||
|
|
||||||
void setupTasks();
|
void setupTasks();
|
||||||
|
void setCurrentCurrency(char currency);
|
||||||
|
|
||||||
|
uint getCurrentCurrency();
|
182
src/lib/shared.cpp
Normal file
182
src/lib/shared.cpp
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
#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 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,29 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Adafruit_MCP23X17.h>
|
#include "MCP23017.h"
|
||||||
|
// #include <zlib_turbo.h>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
|
#include <WiFiClientSecure.h>
|
||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
#include <GxEPD2.h>
|
#include <GxEPD2.h>
|
||||||
#include <GxEPD2_BW.h>
|
#include <GxEPD2_BW.h>
|
||||||
|
#include <mbedtls/md.h>
|
||||||
|
#include "esp_crt_bundle.h"
|
||||||
|
#include <Update.h>
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <utils.hpp>
|
#include <utils.hpp>
|
||||||
|
|
||||||
extern Adafruit_MCP23X17 mcp1;
|
#include "defaults.hpp"
|
||||||
#ifdef IS_BTCLOCK_S3
|
|
||||||
extern Adafruit_MCP23X17 mcp2;
|
#define USER_AGENT "BTClock/3.0"
|
||||||
|
|
||||||
|
extern MCP23017 mcp1;
|
||||||
|
#ifdef IS_BTCLOCK_V8
|
||||||
|
extern MCP23017 mcp2;
|
||||||
#endif
|
#endif
|
||||||
extern Preferences preferences;
|
extern Preferences preferences;
|
||||||
extern std::mutex mcpMutex;
|
extern std::mutex mcpMutex;
|
||||||
|
@ -27,33 +37,75 @@ extern std::mutex mcpMutex;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const PROGMEM int SCREEN_BLOCK_HEIGHT = 0;
|
const PROGMEM int SCREEN_BLOCK_HEIGHT = 0;
|
||||||
const PROGMEM int SCREEN_MSCW_TIME = 1;
|
|
||||||
const PROGMEM int SCREEN_BTC_TICKER = 2;
|
|
||||||
const PROGMEM int SCREEN_TIME = 3;
|
const PROGMEM int SCREEN_TIME = 3;
|
||||||
const PROGMEM int SCREEN_HALVING_COUNTDOWN = 4;
|
const PROGMEM int SCREEN_HALVING_COUNTDOWN = 4;
|
||||||
const PROGMEM int SCREEN_MARKET_CAP = 5;
|
|
||||||
const PROGMEM int SCREEN_BLOCK_FEE_RATE = 6;
|
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_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_BITAXE_HASHRATE = 80;
|
||||||
|
const PROGMEM int SCREEN_BITAXE_BESTDIFF = 81;
|
||||||
|
|
||||||
|
|
||||||
const PROGMEM int SCREEN_COUNTDOWN = 98;
|
const PROGMEM int SCREEN_COUNTDOWN = 98;
|
||||||
const PROGMEM int SCREEN_CUSTOM = 99;
|
const PROGMEM int SCREEN_CUSTOM = 99;
|
||||||
const int SCREEN_COUNT = 7;
|
const int SCREEN_COUNT = 7;
|
||||||
const PROGMEM int screens[SCREEN_COUNT] = {
|
const PROGMEM int screens[SCREEN_COUNT] = {
|
||||||
SCREEN_BLOCK_HEIGHT, SCREEN_MSCW_TIME, SCREEN_BTC_TICKER,
|
SCREEN_BLOCK_HEIGHT, SCREEN_SATS_PER_CURRENCY, SCREEN_BTC_TICKER,
|
||||||
SCREEN_TIME, SCREEN_HALVING_COUNTDOWN, SCREEN_MARKET_CAP,
|
SCREEN_TIME, SCREEN_HALVING_COUNTDOWN, SCREEN_MARKET_CAP,
|
||||||
SCREEN_BLOCK_FEE_RATE};
|
SCREEN_BLOCK_FEE_RATE};
|
||||||
const int usPerSecond = 1000000;
|
const int usPerSecond = 1000000;
|
||||||
const int usPerMinute = 60 * usPerSecond;
|
const int usPerMinute = 60 * usPerSecond;
|
||||||
|
|
||||||
struct SpiRamAllocator : ArduinoJson::Allocator {
|
// extern const char *github_root_ca;
|
||||||
void* allocate(size_t size) override {
|
extern const char *isrg_root_x1cert;
|
||||||
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM);
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate(void* pointer) override {
|
extern const uint8_t rootca_crt_bundle_start[] asm("_binary_x509_crt_bundle_start");
|
||||||
heap_caps_free(pointer);
|
// 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");
|
||||||
|
|
||||||
void* reallocate(void* ptr, size_t new_size) override {
|
// uint8_t* getOceanIcon();
|
||||||
return heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM);
|
|
||||||
|
// 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;
|
||||||
|
};
|
90
src/lib/timers.cpp
Normal file
90
src/lib/timers.cpp
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
}
|
22
src/lib/timers.hpp
Normal file
22
src/lib/timers.hpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#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();
|
160
src/lib/v2_notify.cpp
Normal file
160
src/lib/v2_notify.cpp
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
#include "v2_notify.hpp"
|
||||||
|
|
||||||
|
using namespace V2Notify;
|
||||||
|
|
||||||
|
namespace V2Notify
|
||||||
|
{
|
||||||
|
WebSocketsClient webSocket;
|
||||||
|
|
||||||
|
TaskHandle_t v2NotifyTaskHandle;
|
||||||
|
|
||||||
|
void setupV2Notify()
|
||||||
|
{
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
JsonDocument response;
|
||||||
|
|
||||||
|
response["type"] = "subscribe";
|
||||||
|
response["eventType"] = "blockfee";
|
||||||
|
size_t responseLength = measureMsgPack(response);
|
||||||
|
uint8_t *buffer = new uint8_t[responseLength];
|
||||||
|
serializeMsgPack(response, buffer, responseLength);
|
||||||
|
webSocket.sendBIN(buffer, responseLength);
|
||||||
|
delete[] buffer;
|
||||||
|
|
||||||
|
buffer = new uint8_t[responseLength];
|
||||||
|
|
||||||
|
response["type"] = "subscribe";
|
||||||
|
response["eventType"] = "blockheight";
|
||||||
|
responseLength = measureMsgPack(response);
|
||||||
|
buffer = new uint8_t[responseLength];
|
||||||
|
serializeMsgPack(response, buffer, responseLength);
|
||||||
|
webSocket.sendBIN(buffer, responseLength);
|
||||||
|
|
||||||
|
delete[] buffer;
|
||||||
|
|
||||||
|
buffer = new uint8_t[responseLength];
|
||||||
|
|
||||||
|
response["type"] = "subscribe";
|
||||||
|
response["eventType"] = "price";
|
||||||
|
|
||||||
|
JsonArray currenciesArray = response["currencies"].to<JsonArray>();
|
||||||
|
|
||||||
|
for (const auto &str : getActiveCurrencies())
|
||||||
|
{
|
||||||
|
currenciesArray.add(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// response["currencies"] = currenciesArray;
|
||||||
|
responseLength = measureMsgPack(response);
|
||||||
|
buffer = new uint8_t[responseLength];
|
||||||
|
serializeMsgPack(response, buffer, responseLength);
|
||||||
|
webSocket.sendBIN(buffer, responseLength);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WStype_TEXT:
|
||||||
|
Serial.printf("[WSc] get text: %s\n", payload);
|
||||||
|
|
||||||
|
// send message to server
|
||||||
|
// webSocket.sendTXT("message here");
|
||||||
|
break;
|
||||||
|
case WStype_BIN:
|
||||||
|
{
|
||||||
|
JsonDocument doc;
|
||||||
|
DeserializationError error = deserializeMsgPack(doc, payload, length);
|
||||||
|
|
||||||
|
V2Notify::handleV2Message(doc);
|
||||||
|
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 handleV2Message(JsonDocument doc)
|
||||||
|
{
|
||||||
|
if (doc.containsKey("blockheight"))
|
||||||
|
{
|
||||||
|
uint newBlockHeight = doc["blockheight"].as<uint>();
|
||||||
|
|
||||||
|
if (newBlockHeight == getBlockHeight())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
processNewBlock(newBlockHeight);
|
||||||
|
}
|
||||||
|
else if (doc.containsKey("blockfee"))
|
||||||
|
{
|
||||||
|
uint medianFee = doc["blockfee"].as<uint>();
|
||||||
|
|
||||||
|
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 setupV2NotifyTask()
|
||||||
|
{
|
||||||
|
xTaskCreate(V2Notify::taskV2Notify, "v2Notify", (6 * 1024), NULL, tskIDLE_PRIORITY,
|
||||||
|
&V2Notify::v2NotifyTaskHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isV2NotifyConnected()
|
||||||
|
{
|
||||||
|
return webSocket.isConnected();
|
||||||
|
}
|
||||||
|
}
|
26
src/lib/v2_notify.hpp
Normal file
26
src/lib/v2_notify.hpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <esp_websocket_client.h>
|
||||||
|
#include "block_notify.hpp"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "lib/screen_handler.hpp"
|
||||||
|
|
||||||
|
namespace V2Notify {
|
||||||
|
extern TaskHandle_t v2NotifyTaskHandle;
|
||||||
|
|
||||||
|
void setupV2NotifyTask();
|
||||||
|
void taskV2Notify(void *pvParameters);
|
||||||
|
|
||||||
|
void setupV2Notify();
|
||||||
|
void onWebsocketV2Event(WStype_t type, uint8_t * payload, size_t length);
|
||||||
|
void handleV2Message(JsonDocument doc);
|
||||||
|
|
||||||
|
bool isV2NotifyConnected();
|
||||||
|
}
|
||||||
|
// void stopV2Notify();
|
||||||
|
// void restartV2Notify();
|
||||||
|
// bool getPriceNotifyInit();
|
||||||
|
// uint getLastPriceUpdate();
|
File diff suppressed because it is too large
Load diff
|
@ -14,6 +14,7 @@
|
||||||
#include "lib/price_notify.hpp"
|
#include "lib/price_notify.hpp"
|
||||||
#include "lib/screen_handler.hpp"
|
#include "lib/screen_handler.hpp"
|
||||||
#include "webserver/OneParamRewrite.hpp"
|
#include "webserver/OneParamRewrite.hpp"
|
||||||
|
#include "lib/mining_pool/pool_factory.hpp"
|
||||||
|
|
||||||
extern TaskHandle_t eventSourceTaskHandle;
|
extern TaskHandle_t eventSourceTaskHandle;
|
||||||
|
|
||||||
|
@ -21,18 +22,25 @@ void stopWebServer();
|
||||||
void setupWebserver();
|
void setupWebserver();
|
||||||
bool processEpdColorSettings(AsyncWebServerRequest *request);
|
bool processEpdColorSettings(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void onApiStatus(AsyncWebServerRequest *request);
|
void onApiStatus(AsyncWebServerRequest *request);
|
||||||
void onApiSystemStatus(AsyncWebServerRequest *request);
|
void onApiSystemStatus(AsyncWebServerRequest *request);
|
||||||
void onApiSetWifiTxPower(AsyncWebServerRequest *request);
|
void onApiSetWifiTxPower(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
|
void onApiScreenControl(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
void onApiShowScreen(AsyncWebServerRequest *request);
|
void onApiShowScreen(AsyncWebServerRequest *request);
|
||||||
|
void onApiShowCurrency(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
void onApiShowText(AsyncWebServerRequest *request);
|
void onApiShowText(AsyncWebServerRequest *request);
|
||||||
|
void onApiIdentify(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
void onApiShowTextAdvanced(AsyncWebServerRequest *request, JsonVariant &json);
|
void onApiShowTextAdvanced(AsyncWebServerRequest *request, JsonVariant &json);
|
||||||
|
|
||||||
void onApiActionPause(AsyncWebServerRequest *request);
|
void onApiActionPause(AsyncWebServerRequest *request);
|
||||||
void onApiActionTimerRestart(AsyncWebServerRequest *request);
|
void onApiActionTimerRestart(AsyncWebServerRequest *request);
|
||||||
void onApiSettingsGet(AsyncWebServerRequest *request);
|
void onApiSettingsGet(AsyncWebServerRequest *request);
|
||||||
void onApiSettingsPost(AsyncWebServerRequest *request);
|
|
||||||
void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json);
|
void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json);
|
||||||
void onApiFullRefresh(AsyncWebServerRequest *request);
|
void onApiFullRefresh(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
|
@ -42,6 +50,11 @@ void onApiLightsSetColor(AsyncWebServerRequest *request);
|
||||||
void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json);
|
void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json);
|
||||||
|
|
||||||
void onApiRestart(AsyncWebServerRequest *request);
|
void onApiRestart(AsyncWebServerRequest *request);
|
||||||
|
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 onIndex(AsyncWebServerRequest *request);
|
||||||
void onNotFound(AsyncWebServerRequest *request);
|
void onNotFound(AsyncWebServerRequest *request);
|
||||||
|
@ -53,3 +66,12 @@ void eventSourceTask(void *pvParameters);
|
||||||
|
|
||||||
void onApiStopDataSources(AsyncWebServerRequest *request);
|
void onApiStopDataSources(AsyncWebServerRequest *request);
|
||||||
void onApiRestartDataSources(AsyncWebServerRequest *request);
|
void onApiRestartDataSources(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
|
#ifdef HAS_FRONTLIGHT
|
||||||
|
void onApiFrontlightOn(AsyncWebServerRequest *request);
|
||||||
|
void onApiFrontlightFlash(AsyncWebServerRequest *request);
|
||||||
|
void onApiFrontlightSetBrightness(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
|
void onApiFrontlightStatus(AsyncWebServerRequest *request);
|
||||||
|
void onApiFrontlightOff(AsyncWebServerRequest *request);
|
||||||
|
#endif
|
177
src/main.cpp
177
src/main.cpp
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2023 Djuri Baars
|
* Copyright 2023-2024 Djuri Baars
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -23,76 +23,131 @@ uint wifiLostConnection;
|
||||||
uint priceNotifyLostConnection = 0;
|
uint priceNotifyLostConnection = 0;
|
||||||
uint blockNotifyLostConnection = 0;
|
uint blockNotifyLostConnection = 0;
|
||||||
|
|
||||||
|
int64_t getUptime() {
|
||||||
|
return esp_timer_get_time() / 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handlePriceNotifyDisconnection() {
|
||||||
|
if (priceNotifyLostConnection == 0) {
|
||||||
|
priceNotifyLostConnection = getUptime();
|
||||||
|
Serial.println(F("Lost price notification connection, trying to reconnect..."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((getUptime() - priceNotifyLostConnection) > 300) { // 5 minutes timeout
|
||||||
|
Serial.println(F("Price notification connection lost for 5 minutes, restarting handler..."));
|
||||||
|
restartPriceNotify();
|
||||||
|
priceNotifyLostConnection = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleBlockNotifyDisconnection() {
|
||||||
|
if (blockNotifyLostConnection == 0) {
|
||||||
|
blockNotifyLostConnection = getUptime();
|
||||||
|
Serial.println(F("Lost block notification connection, trying to reconnect..."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((getUptime() - blockNotifyLostConnection) > 300) { // 5 minutes timeout
|
||||||
|
Serial.println(F("Block notification connection lost for 5 minutes, restarting handler..."));
|
||||||
|
restartBlockNotify();
|
||||||
|
blockNotifyLostConnection = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleFrontlight() {
|
||||||
|
#ifdef HAS_FRONTLIGHT
|
||||||
|
if (hasLightLevel() && preferences.getUInt("luxLightToggle", DEFAULT_LUX_LIGHT_TOGGLE) != 0) {
|
||||||
|
uint lightLevel = getLightLevel();
|
||||||
|
uint luxThreshold = preferences.getUInt("luxLightToggle", DEFAULT_LUX_LIGHT_TOGGLE);
|
||||||
|
|
||||||
|
if (lightLevel <= 1 && preferences.getBool("flOffWhenDark", DEFAULT_FL_OFF_WHEN_DARK)) {
|
||||||
|
if (frontlightIsOn()) frontlightFadeOutAll();
|
||||||
|
} else if (lightLevel < luxThreshold && !frontlightIsOn()) {
|
||||||
|
frontlightFadeInAll();
|
||||||
|
} else if (frontlightIsOn() && lightLevel > luxThreshold) {
|
||||||
|
frontlightFadeOutAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkWiFiConnection() {
|
||||||
|
if (!WiFi.isConnected()) {
|
||||||
|
if (!wifiLostConnection) {
|
||||||
|
wifiLostConnection = getUptime();
|
||||||
|
Serial.println(F("Lost WiFi connection, trying to reconnect..."));
|
||||||
|
}
|
||||||
|
if ((getUptime() - wifiLostConnection) > 600) {
|
||||||
|
Serial.println(F("Still no connection after 10 minutes, restarting..."));
|
||||||
|
delay(2000);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
WiFi.begin();
|
||||||
|
} else if (wifiLostConnection) {
|
||||||
|
wifiLostConnection = 0;
|
||||||
|
Serial.println(F("Connection restored, reset timer."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkMissedBlocks() {
|
||||||
|
Serial.println(F("Long time (45 min) since last block, checking if I missed anything..."));
|
||||||
|
int currentBlock = getBlockFetch();
|
||||||
|
if (currentBlock != -1) {
|
||||||
|
if (currentBlock != getBlockHeight()) {
|
||||||
|
Serial.println(F("Detected stuck block height... restarting block handler."));
|
||||||
|
restartBlockNotify();
|
||||||
|
}
|
||||||
|
setLastBlockUpdate(getUptime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void monitorDataConnections() {
|
||||||
|
// Price notification monitoring
|
||||||
|
if (getPriceNotifyInit() && !preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE) && !isPriceNotifyConnected()) {
|
||||||
|
handlePriceNotifyDisconnection();
|
||||||
|
} else if (priceNotifyLostConnection > 0 && isPriceNotifyConnected()) {
|
||||||
|
priceNotifyLostConnection = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block notification monitoring
|
||||||
|
if (getBlockNotifyInit() && !isBlockNotifyConnected()) {
|
||||||
|
handleBlockNotifyDisconnection();
|
||||||
|
} else if (blockNotifyLostConnection > 0 && isBlockNotifyConnected()) {
|
||||||
|
blockNotifyLostConnection = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for missed price updates
|
||||||
|
if ((getLastPriceUpdate(CURRENCY_USD) - getUptime()) > (preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE) * 5)) {
|
||||||
|
Serial.println(F("Detected 5 missed price updates... restarting price handler."));
|
||||||
|
restartPriceNotify();
|
||||||
|
priceNotifyLostConnection = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for missed blocks
|
||||||
|
if ((getLastBlockUpdate() - getUptime()) > 45 * 60) {
|
||||||
|
checkMissedBlocks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" void app_main() {
|
extern "C" void app_main() {
|
||||||
initArduino();
|
initArduino();
|
||||||
|
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// vTaskList(ptrTaskList);
|
if (eventSourceTaskHandle != NULL) {
|
||||||
// Serial.println(F("**********************************"));
|
|
||||||
// Serial.println(F("Task State Prio Stack Num"));
|
|
||||||
// Serial.println(F("**********************************"));
|
|
||||||
// Serial.print(ptrTaskList);
|
|
||||||
// Serial.println(F("**********************************"));
|
|
||||||
if (eventSourceTaskHandle != NULL)
|
|
||||||
xTaskNotifyGive(eventSourceTaskHandle);
|
xTaskNotifyGive(eventSourceTaskHandle);
|
||||||
|
|
||||||
int64_t currentUptime = esp_timer_get_time() / 1000000;;
|
|
||||||
|
|
||||||
if (!WiFi.isConnected()) {
|
|
||||||
if (!wifiLostConnection) {
|
|
||||||
wifiLostConnection = currentUptime;
|
|
||||||
Serial.println("Lost WiFi connection, trying to reconnect...");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((currentUptime - wifiLostConnection) > 600) {
|
if (!getIsOTAUpdating()) {
|
||||||
Serial.println("Still no connection after 10 minutes, restarting...");
|
handleFrontlight();
|
||||||
delay(2000);
|
checkWiFiConnection();
|
||||||
ESP.restart();
|
monitorDataConnections();
|
||||||
|
|
||||||
|
if (getUptime() - getLastTimeSync() > 24 * 60 * 60) {
|
||||||
|
Serial.println(F("Last time update is longer than 24 hours ago, sync again"));
|
||||||
|
syncTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
WiFi.begin();
|
|
||||||
} else if (wifiLostConnection) {
|
|
||||||
wifiLostConnection = 0;
|
|
||||||
Serial.println("Connection restored, reset timer.");
|
|
||||||
} else if (getPriceNotifyInit() && !preferences.getBool("fetchEurPrice", false) && !isPriceNotifyConnected()) {
|
|
||||||
priceNotifyLostConnection++;
|
|
||||||
Serial.println("Lost price data connection...");
|
|
||||||
queueLedEffect(LED_DATA_PRICE_ERROR);
|
|
||||||
|
|
||||||
// if price WS connection does not come back after 6*5 seconds, destroy and recreate
|
|
||||||
if (priceNotifyLostConnection > 6) {
|
|
||||||
Serial.println("Restarting price handler...");
|
|
||||||
|
|
||||||
stopPriceNotify();
|
|
||||||
setupPriceNotify();
|
|
||||||
priceNotifyLostConnection = 0;
|
|
||||||
}
|
|
||||||
} else if (getBlockNotifyInit() && !isBlockNotifyConnected()) {
|
|
||||||
blockNotifyLostConnection++;
|
|
||||||
Serial.println("Lost block data connection...");
|
|
||||||
queueLedEffect(LED_DATA_BLOCK_ERROR);
|
|
||||||
// if mempool WS connection does not come back after 6*5 seconds, destroy and recreate
|
|
||||||
if (blockNotifyLostConnection > 6) {
|
|
||||||
Serial.println("Restarting block handler...");
|
|
||||||
|
|
||||||
stopBlockNotify();
|
|
||||||
setupBlockNotify();
|
|
||||||
blockNotifyLostConnection = 0;
|
|
||||||
}
|
|
||||||
} else if (blockNotifyLostConnection > 0 || priceNotifyLostConnection > 0) {
|
|
||||||
blockNotifyLostConnection = 0;
|
|
||||||
priceNotifyLostConnection = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if more than 5 price updates are missed, there is probably something wrong, reconnect
|
|
||||||
if ((getLastPriceUpdate() - currentUptime) > (preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE)*5)) {
|
|
||||||
stopPriceNotify();
|
|
||||||
setupPriceNotify();
|
|
||||||
|
|
||||||
priceNotifyLostConnection = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||||
|
|
|
@ -1,16 +1,31 @@
|
||||||
#include <data_handler.hpp>
|
#include <data_handler.hpp>
|
||||||
#include <unity.h>
|
#include <unity.h>
|
||||||
|
|
||||||
void setUp(void) {
|
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)
|
||||||
|
{
|
||||||
// set stuff up here
|
// set stuff up here
|
||||||
}
|
}
|
||||||
|
|
||||||
void tearDown(void) {
|
void tearDown(void)
|
||||||
|
{
|
||||||
// clean stuff up here
|
// clean stuff up here
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_CorrectSatsPerDollarConversion(void) {
|
void test_CorrectSatsPerDollarConversion(void)
|
||||||
std::array<std::string, NUM_SCREENS> output = parseSatsPerCurrency(37253, '$', false);
|
{
|
||||||
|
std::array<std::string, NUM_SCREENS> output = parseSatsPerCurrency(37253, CURRENCY_USD, false);
|
||||||
TEST_ASSERT_EQUAL_STRING("MSCW/TIME", output[0].c_str());
|
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("2", output[NUM_SCREENS - 4].c_str());
|
||||||
TEST_ASSERT_EQUAL_STRING("6", output[NUM_SCREENS - 3].c_str());
|
TEST_ASSERT_EQUAL_STRING("6", output[NUM_SCREENS - 3].c_str());
|
||||||
|
@ -18,20 +33,43 @@ void test_CorrectSatsPerDollarConversion(void) {
|
||||||
TEST_ASSERT_EQUAL_STRING("4", output[NUM_SCREENS - 1].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_SixCharacterBlockHeight(void) {
|
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)
|
||||||
|
{
|
||||||
std::array<std::string, NUM_SCREENS> output = parseBlockHeight(999999);
|
std::array<std::string, NUM_SCREENS> output = parseBlockHeight(999999);
|
||||||
TEST_ASSERT_EQUAL_STRING("BLOCK/HEIGHT", output[0].c_str());
|
TEST_ASSERT_EQUAL_STRING("BLOCK/HEIGHT", output[0].c_str());
|
||||||
TEST_ASSERT_EQUAL_STRING("9", output[1].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);
|
std::array<std::string, NUM_SCREENS> output = parseBlockHeight(1000000);
|
||||||
TEST_ASSERT_EQUAL_STRING("1", output[0].c_str());
|
TEST_ASSERT_EQUAL_STRING("1", output[0].c_str());
|
||||||
TEST_ASSERT_EQUAL_STRING("0", output[1].c_str());
|
TEST_ASSERT_EQUAL_STRING("0", output[1].c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_FeeRateDisplay(void) {
|
void test_FeeRateDisplay(void)
|
||||||
|
{
|
||||||
uint testValue = 21;
|
uint testValue = 21;
|
||||||
std::array<std::string, NUM_SCREENS> output = parseBlockFees(static_cast<std::uint16_t>(testValue));
|
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("FEE/RATE", output[0].c_str());
|
||||||
|
@ -40,14 +78,15 @@ void test_FeeRateDisplay(void) {
|
||||||
TEST_ASSERT_EQUAL_STRING("sat/vB", output[NUM_SCREENS - 1].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, '$');
|
std::array<std::string, NUM_SCREENS> output = parsePriceData(100000, '$');
|
||||||
TEST_ASSERT_EQUAL_STRING("$", output[0].c_str());
|
TEST_ASSERT_EQUAL_STRING("$", output[0].c_str());
|
||||||
TEST_ASSERT_EQUAL_STRING("1", output[1].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, '$');
|
std::array<std::string, NUM_SCREENS> output = parsePriceData(1000000, '$');
|
||||||
TEST_ASSERT_EQUAL_STRING("BTC/USD", output[0].c_str());
|
TEST_ASSERT_EQUAL_STRING("BTC/USD", output[0].c_str());
|
||||||
|
|
||||||
|
@ -58,7 +97,83 @@ void test_PriceOf1MillionUsd(void) {
|
||||||
TEST_ASSERT_EQUAL_STRING("M", output[NUM_SCREENS - 1].c_str());
|
TEST_ASSERT_EQUAL_STRING("M", output[NUM_SCREENS - 1].c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_McapLowerUsd(void) {
|
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)
|
||||||
|
{
|
||||||
std::array<std::string, NUM_SCREENS> output = parseMarketCap(810000, 26000, '$', true);
|
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("USD/MCAP", output[0].c_str());
|
||||||
|
|
||||||
|
@ -70,7 +185,8 @@ void test_McapLowerUsd(void) {
|
||||||
TEST_ASSERT_EQUAL_STRING("B", output[NUM_SCREENS - 1].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);
|
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("USD/MCAP", output[0].c_str());
|
||||||
|
|
||||||
|
@ -82,10 +198,29 @@ void test_Mcap1TrillionUsd(void) {
|
||||||
TEST_ASSERT_EQUAL_STRING("T", output[NUM_SCREENS - 1].c_str());
|
TEST_ASSERT_EQUAL_STRING("T", output[NUM_SCREENS - 1].c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_Mcap1TrillionEur(void) {
|
void test_Mcap1TrillionUsdSmallChars(void)
|
||||||
std::array<std::string, NUM_SCREENS> output = parseMarketCap(831000, 52000, '[', true);
|
{
|
||||||
|
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);
|
||||||
TEST_ASSERT_EQUAL_STRING("EUR/MCAP", output[0].c_str());
|
TEST_ASSERT_EQUAL_STRING("EUR/MCAP", output[0].c_str());
|
||||||
TEST_ASSERT_EQUAL_STRING("[", output[NUM_SCREENS-6].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("1", output[NUM_SCREENS - 5].c_str());
|
||||||
TEST_ASSERT_EQUAL_STRING(".", output[NUM_SCREENS - 4].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 - 3].c_str());
|
||||||
|
@ -93,26 +228,83 @@ void test_Mcap1TrillionEur(void) {
|
||||||
TEST_ASSERT_EQUAL_STRING("T", output[NUM_SCREENS - 1].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());
|
||||||
|
}
|
||||||
|
|
||||||
// not needed when using generate_test_runner.rb
|
// not needed when using generate_test_runner.rb
|
||||||
int runUnityTests(void) {
|
int runUnityTests(void)
|
||||||
|
{
|
||||||
UNITY_BEGIN();
|
UNITY_BEGIN();
|
||||||
RUN_TEST(test_CorrectSatsPerDollarConversion);
|
RUN_TEST(test_CorrectSatsPerDollarConversion);
|
||||||
|
RUN_TEST(test_CorrectSatsPerPoundConversion);
|
||||||
|
RUN_TEST(test_SatsPerDollarAfter1B);
|
||||||
RUN_TEST(test_SixCharacterBlockHeight);
|
RUN_TEST(test_SixCharacterBlockHeight);
|
||||||
RUN_TEST(test_SevenCharacterBlockHeight);
|
RUN_TEST(test_SevenCharacterBlockHeight);
|
||||||
RUN_TEST(test_FeeRateDisplay);
|
RUN_TEST(test_FeeRateDisplay);
|
||||||
RUN_TEST(test_PriceOf100kusd);
|
RUN_TEST(test_PriceOf100kusd);
|
||||||
RUN_TEST(test_McapLowerUsd);
|
RUN_TEST(test_McapLowerUsd);
|
||||||
RUN_TEST(test_Mcap1TrillionUsd);
|
RUN_TEST(test_Mcap1TrillionUsd);
|
||||||
|
RUN_TEST(test_Mcap1TrillionUsdSmallChars);
|
||||||
RUN_TEST(test_Mcap1TrillionEur);
|
RUN_TEST(test_Mcap1TrillionEur);
|
||||||
//RUN_TEST(test_Mcap1MillionEur);
|
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);
|
||||||
|
|
||||||
return UNITY_END();
|
return UNITY_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void)
|
||||||
|
{
|
||||||
return runUnityTests();
|
return runUnityTests();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void app_main() {
|
extern "C" void app_main()
|
||||||
|
{
|
||||||
runUnityTests();
|
runUnityTests();
|
||||||
}
|
}
|
||||||
|
|
75
test/test_mining_pool/test_main.cpp
Normal file
75
test/test_mining_pool/test_main.cpp
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#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
Normal file
BIN
x509_crt_bundle
Normal file
Binary file not shown.
Loading…
Reference in a new issue