Compare commits
No commits in common. "main" and "feature/test_new_ci" have entirely different histories.
main
...
feature/te
66 changed files with 9953 additions and 7319 deletions
37
README.md
37
README.md
|
@ -16,44 +16,15 @@ Biggest differences with v2 are:
|
|||
|
||||
New features:
|
||||
- BitAxe integration
|
||||
- Nostr Zap notifier
|
||||
- Multiple mining pool stats integrations
|
||||
- Zap notifier
|
||||
-
|
||||
|
||||
"Steal focus on new block" means that when a new block is mined, the display will switch to the block height screen if it's not on it already.
|
||||
|
||||
See the [docs](https://git.btclock.dev/btclock/docs) repo for more information and building instructions.
|
||||
Most [information](https://github.com/btclock/btclock_v2/wiki) about BTClock v2 is still valid for this version.
|
||||
|
||||
**NOTE**: The software assumes that the hardware is run in a controlled private network. ~~The Web UI and the OTA update mechanism are not password protected and accessible to anyone in the network. Also, since the device only fetches numbers through WebSockets it will skip server certificate verification to save resources.~~ Since 3.2.0 the WebUI is password protectable and all certificates are verified. OTA update mechanism is not password-protected.
|
||||
|
||||
## Building
|
||||
|
||||
Use PlatformIO to build it yourself. Make sure you fetch the [WebUI](https://git.btclock.dev/btclock/webui) submodule.
|
||||
|
||||
|
||||
## Mining pool stats
|
||||
Enable mining pool stats by accessing your btclock's web UI (point a web browser at the device's IP address).
|
||||
|
||||
Under Settings -> Extra Features: toggle Enable Mining Pool Stats.
|
||||
|
||||
New options will appear. Select your mining pool and enter your pool username (Ocean) or api key (Braiins).
|
||||
|
||||
The Mining Pool Earnings screen displays:
|
||||
* Braiins: Today's mining reward thus far
|
||||
* Ocean: Your estimated earnings if the pool were to find a block right now
|
||||
|
||||
For solo mining pools, there are no earning estimations. Your username is the onchain withdrawal address, without the worker name.
|
||||
|
||||
|
||||
### Braiins Pool integration
|
||||
Create an API key based on the steps [here](https://academy.braiins.com/en/braiins-pool/monitoring/#api-configuration).
|
||||
|
||||
The key's permissions should be:
|
||||
* Web Access: no
|
||||
* API Access: yes
|
||||
* Access Permissions: Read-only
|
||||
|
||||
Copy the token that is created for the new key. Enter this as your "Mining Pool username or api key" in the btclock web UI.
|
||||
|
||||
|
||||
### Ocean integration
|
||||
Your "Mining Pool username" is just the onchain withdrawal address that you specify when pointing your miners at Ocean.
|
||||
Use PlatformIO to build it yourself. Make sure you fetch the [WebUI](https://github.com/btclock/webui) submodule.
|
2
data
2
data
|
@ -1 +1 @@
|
|||
Subproject commit 033fe098295ab6da6568d6298b4380e51bec0b98
|
||||
Subproject commit 85b9b17506f89696b89ab6f6e6ed231b7a8f6e91
|
|
@ -164,82 +164,3 @@ int64_t getAmountInSatoshis(std::string bolt11) {
|
|||
|
||||
return satoshis;
|
||||
}
|
||||
|
||||
void parseHashrateString(const std::string& hashrate, std::string& label, std::string& output, unsigned int maxCharacters) {
|
||||
// Handle empty string or "0" cases
|
||||
if (hashrate.empty() || hashrate == "0") {
|
||||
label = "H/S";
|
||||
output = "0";
|
||||
return;
|
||||
}
|
||||
|
||||
size_t suffixLength = 0;
|
||||
if (hashrate.length() > 21) {
|
||||
label = "ZH/S";
|
||||
suffixLength = 21;
|
||||
} else if (hashrate.length() > 18) {
|
||||
label = "EH/S";
|
||||
suffixLength = 18;
|
||||
} else if (hashrate.length() > 15) {
|
||||
label = "PH/S";
|
||||
suffixLength = 15;
|
||||
} else if (hashrate.length() > 12) {
|
||||
label = "TH/S";
|
||||
suffixLength = 12;
|
||||
} else if (hashrate.length() > 9) {
|
||||
label = "GH/S";
|
||||
suffixLength = 9;
|
||||
} else if (hashrate.length() > 6) {
|
||||
label = "MH/S";
|
||||
suffixLength = 6;
|
||||
} else if (hashrate.length() > 3) {
|
||||
label = "KH/S";
|
||||
suffixLength = 3;
|
||||
} else {
|
||||
label = "H/S";
|
||||
suffixLength = 0;
|
||||
}
|
||||
|
||||
double value = std::stod(hashrate) / std::pow(10, suffixLength);
|
||||
|
||||
// Calculate integer part length
|
||||
int integerPartLength = std::to_string(static_cast<int>(value)).length();
|
||||
|
||||
// Calculate remaining space for decimals
|
||||
int remainingSpace = maxCharacters - integerPartLength;
|
||||
|
||||
char buffer[32];
|
||||
if (remainingSpace <= 0)
|
||||
{
|
||||
// No space for decimals, just round to integer
|
||||
snprintf(buffer, sizeof(buffer), "%.0f", value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Space for decimal point and some decimals
|
||||
snprintf(buffer, sizeof(buffer), "%.*f", remainingSpace - 1, value);
|
||||
}
|
||||
|
||||
// Remove trailing zeros and decimal point if necessary
|
||||
output = buffer;
|
||||
if (output.find('.') != std::string::npos)
|
||||
{
|
||||
output = output.substr(0, output.find_last_not_of('0') + 1);
|
||||
if (output.back() == '.')
|
||||
{
|
||||
output.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int getHashrateMultiplier(char unit) {
|
||||
if (unit == '0')
|
||||
return 0;
|
||||
|
||||
static const std::unordered_map<char, int> multipliers = {
|
||||
{'Z', 21}, {'E', 18}, {'P', 15}, {'T', 12},
|
||||
{'G', 9}, {'M', 6}, {'K', 3}
|
||||
};
|
||||
return multipliers.at(unit);
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
int modulo(int x,int N);
|
||||
|
||||
|
@ -15,5 +13,3 @@ double getSupplyAtBlock(std::uint32_t blockNr);
|
|||
std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters = 4);
|
||||
std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters, bool mowMode);
|
||||
int64_t getAmountInSatoshis(std::string bolt11);
|
||||
void parseHashrateString(const std::string& hashrate, std::string& label, std::string& output, unsigned int maxCharacters);
|
||||
int getHashrateMultiplier(char unit);
|
|
@ -7,13 +7,13 @@
|
|||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
data_dir = data/build_gz
|
||||
default_envs = lolin_s3_mini_213epd, lolin_s3_mini_29epd, btclock_rev_b_213epd, btclock_v8_213epd
|
||||
|
||||
[env]
|
||||
|
||||
|
||||
[btclock_base]
|
||||
platform = espressif32 @ ^6.9.0
|
||||
framework = arduino, espidf
|
||||
|
@ -21,8 +21,6 @@ monitor_speed = 115200
|
|||
monitor_filters = esp32_exception_decoder, colorize
|
||||
board_build.filesystem = littlefs
|
||||
extra_scripts = pre:scripts/pre_script.py, post:scripts/extra_script.py
|
||||
platform_packages =
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower
|
||||
board_build.embed_files =
|
||||
x509_crt_bundle
|
||||
build_flags =
|
||||
|
@ -30,7 +28,6 @@ build_flags =
|
|||
-DLAST_BUILD_TIME=$UNIX_TIME
|
||||
-DARDUINO_USB_CDC_ON_BOOT
|
||||
-DCORE_DEBUG_LEVEL=0
|
||||
-D DEFAULT_BOOT_TEXT=\"BTCLOCK\"
|
||||
-fexceptions
|
||||
build_unflags =
|
||||
-Werror=all
|
||||
|
@ -44,7 +41,7 @@ lib_deps =
|
|||
https://github.com/dsbaars/universal_pin#feature/mcp23017_rt
|
||||
https://github.com/dsbaars/GxEPD2#universal_pin
|
||||
https://github.com/tzapu/WiFiManager.git#v2.0.17
|
||||
https://github.com/dsbaars/nostrduino#feature/fix-btclock
|
||||
rblb/Nostrduino@1.2.8
|
||||
|
||||
[env:lolin_s3_mini]
|
||||
extends = btclock_base
|
||||
|
@ -62,9 +59,7 @@ build_flags =
|
|||
-D IS_HW_REV_A
|
||||
build_unflags =
|
||||
${btclock_base.build_unflags}
|
||||
platform_packages =
|
||||
platformio/tool-mklittlefs@^1.203.210628
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||
|
||||
|
||||
[env:btclock_rev_b]
|
||||
extends = btclock_base
|
||||
|
@ -88,9 +83,6 @@ lib_deps =
|
|||
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]
|
||||
extends = env:lolin_s3_mini
|
||||
|
@ -100,9 +92,7 @@ build_flags =
|
|||
-D USE_QR
|
||||
-D VERSION_EPD_2_13
|
||||
-D HW_REV=\"REV_A_EPD_2_13\"
|
||||
platform_packages =
|
||||
platformio/tool-mklittlefs@^1.203.210628
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||
|
||||
|
||||
[env:btclock_rev_b_213epd]
|
||||
extends = env:btclock_rev_b
|
||||
|
@ -112,9 +102,6 @@ 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]
|
||||
extends = env:lolin_s3_mini
|
||||
|
@ -124,9 +111,6 @@ build_flags =
|
|||
-D USE_QR
|
||||
-D VERSION_EPD_2_9
|
||||
-D HW_REV=\"REV_A_EPD_2_9\"
|
||||
platform_packages =
|
||||
platformio/tool-mklittlefs@^1.203.210628
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||
|
||||
[env:btclock_rev_b_29epd]
|
||||
extends = env:btclock_rev_b
|
||||
|
@ -136,9 +120,6 @@ 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
|
||||
|
@ -165,9 +146,6 @@ build_flags =
|
|||
-D MCP2_A2_PIN=14
|
||||
build_unflags =
|
||||
${btclock_base.build_unflags}
|
||||
platform_packages =
|
||||
platformio/tool-mklittlefs@^1.203.210628
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||
|
||||
[env:btclock_v8_213epd]
|
||||
extends = env:btclock_v8
|
||||
|
@ -177,9 +155,6 @@ 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]
|
||||
platform = native
|
||||
|
@ -190,8 +165,3 @@ build_flags =
|
|||
-D NEOPIXEL_PIN=34
|
||||
-D NEOPIXEL_COUNT=4
|
||||
-D NUM_SCREENS=7
|
||||
-D UNITY_TEST
|
||||
-std=gnu++17
|
||||
platform_packages =
|
||||
platformio/tool-mklittlefs@^1.203.210628
|
||||
earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
]
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
Import("env")
|
||||
import os
|
||||
import gzip
|
||||
from shutil import copyfileobj, rmtree, copyfile, copytree
|
||||
from shutil import copyfileobj, rmtree
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
|
@ -29,7 +29,7 @@ def process_directory(input_dir, output_dir):
|
|||
Path(output_root).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for file in files:
|
||||
# if not file.endswith(('.bin')):
|
||||
# if file.endswith(('.html', '.css', '.js')):
|
||||
input_file_path = os.path.join(root, file)
|
||||
output_file_path = os.path.join(output_root, file + '.gz')
|
||||
gzip_file(input_file_path, output_file_path)
|
||||
|
@ -41,72 +41,11 @@ def process_directory(input_dir, output_dir):
|
|||
|
||||
# Build web interface before building FS
|
||||
def before_buildfs(source, target, env):
|
||||
|
||||
env.Execute("cd data && yarn && yarn postinstall && yarn build")
|
||||
input_directory = 'data/dist'
|
||||
output_directory = 'data/build_gz'
|
||||
# copytree("assets", "data/dist/assets")
|
||||
|
||||
process_directory(input_directory, output_directory)
|
||||
|
||||
def get_fs_partition_size(env):
|
||||
import csv
|
||||
|
||||
# Get partition table path - first try custom, then default
|
||||
board_config = env.BoardConfig()
|
||||
partition_table = board_config.get("build.partitions", "default.csv")
|
||||
|
||||
# Handle default partition table path
|
||||
if partition_table == "default.csv" or partition_table == "huge_app.csv":
|
||||
partition_table = os.path.join(env.PioPlatform().get_package_dir("framework-arduinoespressif32"),
|
||||
"tools", "partitions", partition_table)
|
||||
|
||||
# Parse CSV to find spiffs/littlefs partition
|
||||
with open(partition_table, 'r') as f:
|
||||
for row in csv.reader(f):
|
||||
if len(row) < 5:
|
||||
continue
|
||||
# Remove comments and whitespace
|
||||
row = [cell.strip().split('#')[0] for cell in row]
|
||||
# Check if this is a spiffs or littlefs partition
|
||||
if row[0].startswith(('spiffs', 'littlefs')):
|
||||
# Size is in hex format
|
||||
return int(row[4], 16)
|
||||
return 0
|
||||
|
||||
def get_littlefs_used_size(binary_path):
|
||||
mklittlefs_path = os.path.join(env.PioPlatform().get_package_dir("tool-mklittlefs-rp2040-earlephilhower"), "mklittlefs")
|
||||
|
||||
try:
|
||||
result = subprocess.run([mklittlefs_path, '-l', binary_path], capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
# Parse the output to sum up file sizes
|
||||
total_size = 0
|
||||
for line in result.stdout.splitlines():
|
||||
if line.strip() and not line.startswith('<dir>') and not line.startswith('Creation'):
|
||||
# Each line format: size filename
|
||||
size = line.split()[0]
|
||||
total_size += int(size)
|
||||
return total_size
|
||||
except Exception as e:
|
||||
print(f"Error getting filesystem size: {e}")
|
||||
return 0
|
||||
|
||||
|
||||
def after_littlefs(source, target, env):
|
||||
binary_path = str(target[0])
|
||||
partition_size = get_fs_partition_size(env)
|
||||
used_size = get_littlefs_used_size(binary_path)
|
||||
|
||||
percentage = (used_size / partition_size) * 100
|
||||
bar_width = 50
|
||||
filled = int(bar_width * percentage / 100)
|
||||
bar = '=' * filled + '-' * (bar_width - filled)
|
||||
|
||||
print(f"\nLittleFS Actual Usage: [{bar}] {percentage:.1f}% ({used_size}/{partition_size} bytes)")
|
||||
|
||||
|
||||
flash_size = env.BoardConfig().get("upload.flash_size", "4MB")
|
||||
fs_image_name = f"littlefs_{flash_size}"
|
||||
env.Replace(ESP32_FS_IMAGE_NAME=fs_image_name)
|
||||
|
@ -119,7 +58,3 @@ fs_name = env.get("ESP32_FS_IMAGE_NAME", "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
|
|
@ -1,191 +1,384 @@
|
|||
#pragma once
|
||||
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Arduino.h>
|
||||
#include "fonts.hpp"
|
||||
|
||||
const uint8_t Antonio_SemiBold20pt7bBitmaps_Gzip[] = {
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa5, 0x57,
|
||||
0x4f, 0x6f, 0xe3, 0xba, 0x11, 0xa7, 0xaa, 0xe2, 0xf1, 0x1d, 0x16, 0xe6,
|
||||
0xf5, 0x1d, 0xbc, 0x62, 0x3f, 0xc2, 0xeb, 0xcd, 0x8b, 0x55, 0xac, 0xaf,
|
||||
0xf2, 0x4e, 0x7b, 0x76, 0xb0, 0x40, 0x57, 0x46, 0x94, 0x48, 0x81, 0x81,
|
||||
0xea, 0x52, 0xac, 0xaf, 0x5b, 0x60, 0x9b, 0x7c, 0x8d, 0x2d, 0x10, 0x6c,
|
||||
0x64, 0x08, 0x78, 0xbe, 0x45, 0x5f, 0x60, 0x11, 0x53, 0x10, 0x50, 0x5f,
|
||||
0x8a, 0x8a, 0x86, 0x80, 0x4a, 0x41, 0x14, 0xb1, 0x3f, 0xd2, 0x72, 0xfe,
|
||||
0x6c, 0xb2, 0xd9, 0xd7, 0x96, 0xa4, 0x39, 0x43, 0x8a, 0x96, 0x86, 0x33,
|
||||
0xc3, 0xdf, 0x0c, 0x89, 0xea, 0xcb, 0x1b, 0x5d, 0x08, 0xd3, 0x6c, 0x79,
|
||||
0x70, 0x59, 0x7f, 0xf8, 0x95, 0xd8, 0x73, 0x6b, 0x19, 0x15, 0xd1, 0x26,
|
||||
0x99, 0x88, 0xd1, 0x64, 0xe8, 0x0f, 0x5c, 0x3a, 0xa4, 0xef, 0xed, 0x98,
|
||||
0x9b, 0xe5, 0xed, 0x48, 0xb8, 0x13, 0x67, 0x34, 0x18, 0xd2, 0x81, 0x3d,
|
||||
0x67, 0xaa, 0x56, 0xcd, 0xdd, 0x3a, 0x66, 0xd6, 0x59, 0x69, 0x54, 0x24,
|
||||
0xfb, 0xc2, 0x17, 0xe4, 0x07, 0xf2, 0xb3, 0x15, 0xbd, 0x20, 0xbf, 0xf0,
|
||||
0x85, 0x3a, 0x6d, 0xaf, 0xeb, 0xf8, 0x6c, 0x2f, 0x2f, 0x99, 0x7f, 0x50,
|
||||
0xac, 0x48, 0x60, 0x35, 0x4c, 0x78, 0x51, 0x60, 0x49, 0x9a, 0x8c, 0x65,
|
||||
0x1d, 0xb7, 0x5e, 0xae, 0x58, 0x7b, 0xa0, 0xb2, 0xce, 0x13, 0x9d, 0x95,
|
||||
0x0c, 0xc8, 0xc4, 0x8e, 0x86, 0x84, 0x47, 0x56, 0x42, 0x3b, 0xe2, 0x92,
|
||||
0x50, 0xd8, 0x09, 0x3b, 0x25, 0x23, 0x22, 0x25, 0x8d, 0x18, 0x23, 0x13,
|
||||
0x30, 0x8c, 0x80, 0x91, 0x60, 0x06, 0x60, 0x2c, 0x01, 0x66, 0x08, 0xc6,
|
||||
0x4e, 0x7e, 0x91, 0xd2, 0xf5, 0x32, 0x66, 0xc7, 0x9d, 0x94, 0xbe, 0x2a,
|
||||
0x19, 0x9d, 0x0f, 0x24, 0x8a, 0x62, 0x6c, 0xc0, 0xa4, 0x14, 0xa2, 0x5e,
|
||||
0x39, 0x03, 0x16, 0xe6, 0x42, 0x54, 0xed, 0x70, 0xc0, 0xf8, 0x2c, 0x11,
|
||||
0x92, 0x8c, 0x06, 0x8c, 0xd0, 0x08, 0x8c, 0xaf, 0x19, 0x02, 0x46, 0x82,
|
||||
0x19, 0x80, 0xb1, 0x04, 0xbe, 0xe2, 0x90, 0x8d, 0xb0, 0x12, 0x66, 0x24,
|
||||
0x49, 0xec, 0xc8, 0x6e, 0x6c, 0x61, 0x77, 0x44, 0x45, 0x81, 0x74, 0x86,
|
||||
0x34, 0xb6, 0xf2, 0xc4, 0x9f, 0x80, 0x99, 0x93, 0x2a, 0xda, 0x13, 0xcc,
|
||||
0xb7, 0x5a, 0x72, 0x04, 0xd1, 0x69, 0x62, 0x4b, 0xd2, 0xfd, 0x18, 0xfe,
|
||||
0xd9, 0x2b, 0xe6, 0x6e, 0x3d, 0x9f, 0xe6, 0xad, 0x1f, 0xba, 0x7c, 0x6e,
|
||||
0x17, 0x89, 0x94, 0x63, 0x87, 0xc7, 0x6a, 0xa3, 0x5a, 0x6f, 0x1d, 0x6f,
|
||||
0x84, 0x52, 0xf5, 0x9f, 0xde, 0xbc, 0x19, 0x2b, 0x95, 0x39, 0xae, 0xbf,
|
||||
0x29, 0x96, 0xf3, 0x27, 0xc8, 0x75, 0x9d, 0x27, 0x9b, 0x0a, 0xc3, 0xec,
|
||||
0x74, 0x1c, 0x5c, 0x55, 0xeb, 0x47, 0xe4, 0xa0, 0x2e, 0x3f, 0x40, 0xf2,
|
||||
0xe1, 0xef, 0xf7, 0x5f, 0x5d, 0x5c, 0x1f, 0xaa, 0x45, 0x6b, 0x0b, 0xaf,
|
||||
0x28, 0xd5, 0xdb, 0xbf, 0xfe, 0x4a, 0x7f, 0x1c, 0x42, 0x5f, 0x50, 0x15,
|
||||
0x9a, 0x36, 0xa0, 0xd6, 0x5c, 0xcf, 0xff, 0x65, 0xfd, 0xc7, 0x8f, 0x09,
|
||||
0x28, 0x44, 0x20, 0x0d, 0x8d, 0x5c, 0x4b, 0x0c, 0x88, 0xd4, 0x34, 0x71,
|
||||
0x34, 0x1d, 0xd9, 0x5b, 0xea, 0x1b, 0xca, 0x88, 0xa6, 0xd0, 0x87, 0xa6,
|
||||
0xae, 0xa1, 0x34, 0x22, 0x4c, 0x9b, 0x56, 0xd5, 0x73, 0x63, 0xc0, 0x26,
|
||||
0x4c, 0x3b, 0x2e, 0x55, 0xfc, 0xed, 0xc1, 0xd9, 0x61, 0x5d, 0x76, 0x9e,
|
||||
0x54, 0xc4, 0x9e, 0xa5, 0x85, 0x96, 0xe6, 0x94, 0xc5, 0x69, 0x2e, 0x9b,
|
||||
0x60, 0xcc, 0x9f, 0x62, 0x12, 0x76, 0xac, 0x78, 0xa3, 0xca, 0x79, 0x70,
|
||||
0x90, 0x5e, 0x3a, 0xdb, 0x97, 0xc1, 0x83, 0x7c, 0x9a, 0xb8, 0xb6, 0x70,
|
||||
0x2c, 0xc8, 0x04, 0xb1, 0x03, 0xb8, 0x90, 0x2d, 0xb8, 0x96, 0x34, 0x00,
|
||||
0xed, 0xe0, 0x93, 0x11, 0x4b, 0x54, 0xac, 0x78, 0xe7, 0x5f, 0xe7, 0x65,
|
||||
0x7c, 0xe6, 0xc8, 0x83, 0x85, 0xa0, 0x91, 0x03, 0xc1, 0x85, 0x67, 0x35,
|
||||
0x54, 0xf0, 0x84, 0x91, 0x31, 0xf1, 0x2d, 0x41, 0xf1, 0x2c, 0x5c, 0x28,
|
||||
0x4d, 0x0e, 0xf2, 0xf5, 0xea, 0xb4, 0xf5, 0x44, 0x48, 0xb4, 0x4f, 0x7a,
|
||||
0xa4, 0xb5, 0x25, 0x4f, 0x42, 0xab, 0xfd, 0x41, 0xbe, 0x4e, 0x4a, 0xfb,
|
||||
0x6c, 0x28, 0xdf, 0x2e, 0x32, 0xe6, 0x8e, 0x24, 0x2c, 0x32, 0x74, 0x27,
|
||||
0x9b, 0x34, 0x63, 0x23, 0xb7, 0x3f, 0x41, 0x44, 0xc2, 0x3b, 0xe1, 0x1b,
|
||||
0x20, 0xaa, 0xec, 0xd0, 0x7a, 0x0d, 0x33, 0x52, 0xb1, 0x16, 0xc3, 0xab,
|
||||
0x92, 0xcb, 0xcd, 0x6e, 0x4a, 0x32, 0xb8, 0xa2, 0x69, 0xfc, 0xba, 0xec,
|
||||
0xc2, 0xb4, 0x81, 0xa8, 0x33, 0xc5, 0x5b, 0xff, 0x0a, 0xa2, 0x7e, 0x76,
|
||||
0x9a, 0x83, 0xbc, 0x8c, 0xc6, 0xa4, 0xb1, 0xcb, 0xb9, 0xf2, 0x3a, 0x75,
|
||||
0xbd, 0x9d, 0x0c, 0x73, 0x15, 0x77, 0x7c, 0x47, 0xa6, 0xf9, 0x5a, 0x9d,
|
||||
0x42, 0x8b, 0x61, 0xb4, 0x13, 0x20, 0x80, 0x39, 0x22, 0x08, 0x40, 0x13,
|
||||
0xb3, 0xc9, 0x27, 0x78, 0x4e, 0xf4, 0x59, 0x83, 0x0e, 0x1a, 0x3d, 0x85,
|
||||
0x0f, 0x10, 0x1c, 0x4a, 0xde, 0x5e, 0x2b, 0x58, 0x6e, 0xd1, 0x31, 0x19,
|
||||
0xce, 0x5a, 0x2e, 0x60, 0x9f, 0xa9, 0x5a, 0xb6, 0x5e, 0xa1, 0x4e, 0xf6,
|
||||
0x36, 0x8a, 0x36, 0xf7, 0x9e, 0xd0, 0xd6, 0x33, 0x96, 0xeb, 0x08, 0x87,
|
||||
0x49, 0x6e, 0x60, 0x12, 0x1f, 0x26, 0xbd, 0x7d, 0xda, 0x2f, 0x5d, 0xae,
|
||||
0x3a, 0x55, 0x29, 0xed, 0x48, 0xbb, 0x27, 0xac, 0x9d, 0xd6, 0xe6, 0x50,
|
||||
0x13, 0x78, 0x1e, 0x41, 0x31, 0x40, 0xa3, 0x22, 0xcd, 0x12, 0x20, 0x4a,
|
||||
0xf5, 0xf1, 0xe2, 0xed, 0x07, 0xf2, 0x87, 0x17, 0xf4, 0x58, 0x76, 0xde,
|
||||
0xe9, 0x52, 0x36, 0x43, 0x5b, 0x1c, 0x51, 0xe9, 0xd9, 0x0d, 0xb7, 0xc4,
|
||||
0x0b, 0xa2, 0x77, 0xd8, 0xf6, 0xff, 0x4a, 0xa2, 0x9f, 0xec, 0x28, 0x60,
|
||||
0x49, 0x47, 0x65, 0x68, 0x0b, 0x97, 0xcd, 0x36, 0x6d, 0xe7, 0xc5, 0x89,
|
||||
0xfc, 0xe9, 0x77, 0x78, 0x53, 0xdc, 0x86, 0x65, 0xb7, 0x29, 0xf9, 0x46,
|
||||
0xf5, 0xcd, 0x92, 0xf0, 0x55, 0xf8, 0x45, 0xeb, 0xcd, 0xa4, 0x0b, 0x87,
|
||||
0x45, 0x33, 0xdf, 0xec, 0x79, 0x3b, 0x21, 0x5e, 0x44, 0x20, 0x12, 0x5e,
|
||||
0xdf, 0x30, 0x6d, 0x3a, 0x0d, 0x25, 0x64, 0xe4, 0x93, 0xe1, 0x88, 0xc7,
|
||||
0xae, 0x57, 0x9c, 0x38, 0x9b, 0x8b, 0xe1, 0x68, 0x3d, 0x18, 0xbe, 0x7b,
|
||||
0x3f, 0xe0, 0x4b, 0xca, 0x8a, 0x59, 0xbc, 0x59, 0xa4, 0x8d, 0xc8, 0xdb,
|
||||
0xc9, 0xa4, 0x1b, 0x8d, 0xc2, 0xa1, 0x7e, 0xf0, 0x6a, 0x49, 0x87, 0xc5,
|
||||
0xf2, 0x62, 0xf2, 0xef, 0xce, 0x0d, 0x3d, 0x87, 0x33, 0x7a, 0x4c, 0xf0,
|
||||
0x62, 0xa0, 0x12, 0xbc, 0x01, 0x10, 0x30, 0xa1, 0xaa, 0x21, 0x0a, 0x2a,
|
||||
0x93, 0x84, 0x26, 0x54, 0xb0, 0xbb, 0x2a, 0x07, 0xd2, 0xd9, 0xd5, 0x89,
|
||||
0xe3, 0x0f, 0x7d, 0x77, 0x57, 0x51, 0x02, 0x90, 0xb0, 0x45, 0xed, 0xc2,
|
||||
0xce, 0x77, 0x1a, 0x54, 0x2c, 0xe3, 0x92, 0x2b, 0x5b, 0xc1, 0x29, 0x5a,
|
||||
0x79, 0x9d, 0x96, 0xc6, 0x63, 0x17, 0x20, 0x63, 0x8c, 0x32, 0xe5, 0x35,
|
||||
0x50, 0x7b, 0xc6, 0xfa, 0xc9, 0x96, 0xdf, 0x3a, 0x75, 0x5a, 0xaa, 0xf3,
|
||||
0x56, 0x15, 0x8a, 0xb0, 0x24, 0x4c, 0xe1, 0x34, 0xc1, 0x54, 0xbb, 0x10,
|
||||
0x6b, 0x3c, 0xa1, 0x66, 0x86, 0x18, 0x5f, 0xe8, 0x5d, 0xe2, 0x76, 0x12,
|
||||
0x84, 0xfb, 0x87, 0xf9, 0xba, 0x9c, 0xc3, 0xe2, 0x5e, 0xa4, 0xa8, 0xf2,
|
||||
0x60, 0x71, 0x1c, 0xe2, 0xc5, 0x23, 0x8b, 0x3f, 0x35, 0x08, 0x15, 0xdc,
|
||||
0x60, 0xdd, 0x12, 0x63, 0x69, 0xe9, 0x02, 0x93, 0x6f, 0x3b, 0x33, 0x95,
|
||||
0x3c, 0x98, 0x33, 0x8b, 0x1e, 0x2f, 0x35, 0x33, 0xf6, 0xc3, 0xa5, 0xba,
|
||||
0x23, 0x4c, 0x6c, 0x77, 0x72, 0x78, 0x4f, 0x5a, 0xd6, 0x6f, 0xe1, 0x4a,
|
||||
0xe9, 0x93, 0xc1, 0x1e, 0xec, 0x84, 0xf9, 0x7b, 0x79, 0x5d, 0x42, 0x47,
|
||||
0xff, 0xf4, 0x7e, 0x36, 0x7a, 0xe9, 0xd5, 0xf3, 0x80, 0xf4, 0xe5, 0xc9,
|
||||
0x67, 0x5b, 0x22, 0xd5, 0xd7, 0x05, 0xd8, 0x93, 0x8c, 0x81, 0x41, 0x06,
|
||||
0x75, 0x9e, 0x1c, 0xdc, 0x83, 0x40, 0x1d, 0xf7, 0xea, 0xac, 0x0d, 0xf3,
|
||||
0x8e, 0xc8, 0x3d, 0x71, 0xbd, 0x58, 0xcf, 0x32, 0x76, 0xe2, 0xb8, 0xae,
|
||||
0xdf, 0x6c, 0xf2, 0x75, 0x9a, 0xc5, 0x27, 0x2c, 0x70, 0x9a, 0x3d, 0x11,
|
||||
0x26, 0xca, 0xea, 0xec, 0x8e, 0x9e, 0xe9, 0xa1, 0x2b, 0xa7, 0x72, 0x93,
|
||||
0x17, 0xe9, 0x72, 0x19, 0xcf, 0xd9, 0x09, 0x77, 0x1d, 0x7f, 0x2c, 0x0f,
|
||||
0x45, 0xf0, 0x48, 0x25, 0x4f, 0x76, 0x46, 0xa1, 0x8d, 0xa5, 0x22, 0xde,
|
||||
0xd8, 0x2a, 0xf1, 0xda, 0x5d, 0x17, 0x76, 0x54, 0x09, 0x74, 0x9d, 0x08,
|
||||
0xff, 0xc5, 0x3a, 0xa9, 0xca, 0x41, 0x37, 0x29, 0xd7, 0xa6, 0x73, 0xba,
|
||||
0x69, 0x56, 0x7c, 0x54, 0xaf, 0xb2, 0xea, 0xa3, 0x7a, 0x9d, 0x55, 0x8d,
|
||||
0xe2, 0xf3, 0xcd, 0x6d, 0x67, 0x22, 0xa8, 0xe9, 0x10, 0xb7, 0x95, 0xdd,
|
||||
0x6e, 0xb5, 0x8b, 0xf3, 0x0d, 0x34, 0x2f, 0x14, 0x1c, 0xa5, 0x55, 0xd5,
|
||||
0x4d, 0x56, 0x9f, 0x5f, 0xaa, 0xd7, 0xe0, 0x56, 0xcb, 0x90, 0xb7, 0xa1,
|
||||
0x54, 0x29, 0x16, 0x18, 0x25, 0xe2, 0x0f, 0xd2, 0x4b, 0x26, 0xb7, 0xc6,
|
||||
0xcb, 0xef, 0x1b, 0xef, 0x19, 0x02, 0x37, 0xac, 0x55, 0xef, 0x86, 0x71,
|
||||
0xef, 0x86, 0x5f, 0x7b, 0x1e, 0x54, 0xac, 0xce, 0x6f, 0x54, 0xd6, 0xf8,
|
||||
0x7d, 0xc0, 0x72, 0xfb, 0x28, 0x65, 0x06, 0xe4, 0xff, 0xf9, 0xa6, 0x83,
|
||||
0x83, 0x8c, 0x38, 0x61, 0x13, 0x6c, 0x55, 0x03, 0xb2, 0xbc, 0x5e, 0xdc,
|
||||
0x9d, 0x3d, 0x73, 0xbe, 0xb8, 0xa7, 0xcf, 0xd7, 0xed, 0xd9, 0x7b, 0x82,
|
||||
0x48, 0x20, 0x7c, 0xaa, 0x83, 0xd1, 0x54, 0x83, 0x39, 0x6f, 0xf0, 0xf6,
|
||||
0x78, 0xc5, 0x8f, 0x48, 0x6b, 0x35, 0xb6, 0x64, 0x82, 0x47, 0x1e, 0xf2,
|
||||
0x85, 0x2b, 0x4c, 0x5e, 0xc2, 0x55, 0x45, 0x3d, 0xbb, 0xdc, 0x7a, 0x6c,
|
||||
0xbf, 0x69, 0x53, 0x58, 0xd4, 0xfc, 0xf6, 0xf6, 0x1b, 0x76, 0xf8, 0xd4,
|
||||
0x01, 0xe9, 0xbc, 0x26, 0x8c, 0x00, 0x33, 0x06, 0x93, 0x1a, 0xc7, 0xd7,
|
||||
0xd5, 0x20, 0x52, 0xe0, 0xf6, 0xc5, 0x0c, 0x9d, 0x5d, 0x9d, 0x38, 0x00,
|
||||
0x31, 0xb6, 0xab, 0x00, 0x35, 0xda, 0x57, 0x38, 0xdf, 0xae, 0x39, 0x52,
|
||||
0x3b, 0xad, 0xe3, 0x1f, 0x7d, 0xdd, 0x80, 0x9f, 0xa3, 0x7a, 0xe8, 0xd6,
|
||||
0xae, 0xbb, 0xbe, 0xdf, 0xce, 0x5e, 0x16, 0x9f, 0x5e, 0xa6, 0xba, 0xf1,
|
||||
0xbe, 0x35, 0xba, 0x2d, 0x1a, 0x76, 0x8c, 0x26, 0xef, 0x37, 0x78, 0x53,
|
||||
0x4d, 0x4f, 0x1c, 0xdf, 0x2f, 0x0a, 0xc4, 0x65, 0x57, 0x86, 0xc7, 0x1d,
|
||||
0xd5, 0x39, 0x25, 0xd4, 0x0a, 0xa3, 0x6b, 0x50, 0x83, 0xde, 0x43, 0x0b,
|
||||
0x93, 0x9e, 0xa8, 0x10, 0xad, 0x5d, 0x7f, 0x3f, 0x5f, 0xc7, 0x27, 0x03,
|
||||
0xfc, 0x4f, 0x36, 0xcc, 0xe7, 0xbe, 0x13, 0x38, 0xae, 0xe3, 0x8e, 0x5d,
|
||||
0x34, 0x30, 0xd8, 0x11, 0xd3, 0x15, 0x7b, 0xa1, 0xba, 0x0a, 0xfb, 0x3b,
|
||||
0x35, 0xac, 0x15, 0xda, 0x2e, 0xca, 0xb8, 0x34, 0x41, 0x1a, 0xe2, 0x1b,
|
||||
0x03, 0x48, 0xfe, 0x68, 0xf6, 0x0e, 0x34, 0xe4, 0x33, 0x45, 0x3f, 0x76,
|
||||
0xec, 0xc4, 0xa7, 0x64, 0x4b, 0x22, 0x4d, 0x5c, 0x4d, 0x2c, 0x43, 0x06,
|
||||
0x96, 0xd0, 0x84, 0x19, 0xe2, 0x6b, 0x62, 0x7c, 0xe3, 0x99, 0xa2, 0x1f,
|
||||
0x27, 0x70, 0xb0, 0x08, 0x8a, 0xb0, 0xbf, 0xd0, 0xb3, 0x81, 0x0f, 0x4c,
|
||||
0x11, 0xc0, 0x14, 0x8d, 0x28, 0x0e, 0x73, 0x35, 0xe4, 0x04, 0x46, 0xac,
|
||||
0xab, 0xf5, 0xfb, 0xd1, 0x7e, 0xfa, 0xc0, 0x53, 0x9d, 0xc6, 0xa4, 0xe7,
|
||||
0x21, 0x6b, 0xc3, 0x6a, 0x7d, 0xc9, 0xbf, 0x4a, 0x42, 0x0e, 0x8b, 0x5a,
|
||||
0xad, 0x94, 0x17, 0xec, 0x8e, 0xdb, 0xb4, 0x58, 0x9d, 0x76, 0x0a, 0xd9,
|
||||
0xc3, 0xf3, 0xc1, 0xa1, 0x85, 0xd2, 0xba, 0xba, 0x3e, 0xd5, 0x89, 0xe3,
|
||||
0x1c, 0x89, 0x63, 0x7c, 0x76, 0x97, 0x1e, 0xde, 0x03, 0xcf, 0x7b, 0xa8,
|
||||
0xb9, 0x5e, 0x19, 0xd4, 0x34, 0xf9, 0x8b, 0x4e, 0x63, 0x0e, 0x2b, 0x7c,
|
||||
0x37, 0xbc, 0x79, 0x20, 0xcc, 0x93, 0x04, 0xe7, 0x6c, 0x05, 0x09, 0xdb,
|
||||
0x43, 0x69, 0x82, 0xdf, 0xdd, 0xb6, 0x0e, 0xcc, 0x73, 0x63, 0x8e, 0xce,
|
||||
0x04, 0xbf, 0x3e, 0xef, 0x8a, 0xe1, 0x4e, 0xd3, 0x6a, 0xad, 0x21, 0x4b,
|
||||
0xd0, 0x74, 0xd3, 0x06, 0x0e, 0x9d, 0xeb, 0x45, 0x1b, 0xe1, 0x3b, 0xf4,
|
||||
0xf8, 0x5b, 0x5d, 0xc4, 0x03, 0xa5, 0x55, 0x01, 0xd4, 0x78, 0x56, 0x96,
|
||||
0x6c, 0x2b, 0x8b, 0xde, 0xc3, 0x34, 0x5d, 0x23, 0x2c, 0xc3, 0x11, 0x77,
|
||||
0xba, 0xab, 0x20, 0xa8, 0x52, 0xe9, 0xf7, 0x02, 0x6b, 0x3f, 0x40, 0xca,
|
||||
0x4e, 0x1e, 0x86, 0x22, 0x89, 0x3b, 0x82, 0x20, 0xec, 0x1b, 0x77, 0x83,
|
||||
0xe0, 0x4a, 0xad, 0x3e, 0xdf, 0x25, 0xa9, 0xdb, 0xd4, 0x00, 0x69, 0x03,
|
||||
0xd7, 0x37, 0x8b, 0x6c, 0xee, 0xee, 0x49, 0x0d, 0x3a, 0x8d, 0x87, 0x44,
|
||||
0x54, 0x63, 0x8f, 0xc4, 0x71, 0xc1, 0xad, 0x23, 0xd0, 0x54, 0xc3, 0x5a,
|
||||
0x98, 0x3c, 0x8e, 0x7d, 0x55, 0x5e, 0xad, 0xb2, 0xd2, 0xb0, 0x0d, 0x12,
|
||||
0x48, 0x08, 0x37, 0x6b, 0x1d, 0x26, 0xfd, 0xff, 0x91, 0x75, 0xab, 0xf5,
|
||||
0xf9, 0x39, 0xde, 0x15, 0x7f, 0x7f, 0xf3, 0x66, 0x80, 0xfc, 0x3e, 0x9c,
|
||||
0xdd, 0x83, 0xd4, 0x6f, 0xba, 0xc1, 0xe5, 0x9d, 0x45, 0xab, 0xe5, 0x39,
|
||||
0x82, 0x84, 0x9a, 0x7f, 0x27, 0x8b, 0xc1, 0x6d, 0x43, 0x21, 0xca, 0x1c,
|
||||
0xdd, 0x86, 0x0f, 0xa7, 0x37, 0xef, 0x73, 0xae, 0xb6, 0x25, 0xfe, 0x61,
|
||||
0x5d, 0xab, 0x4c, 0xf1, 0x9d, 0xa7, 0x5a, 0xb2, 0xba, 0xc6, 0xa6, 0x96,
|
||||
0xb3, 0x85, 0xc0, 0x16, 0x1d, 0x46, 0x1f, 0x31, 0x84, 0x23, 0xd3, 0xba,
|
||||
0x42, 0xf4, 0xf0, 0xcd, 0xfd, 0xa6, 0x5e, 0x7e, 0x26, 0xfa, 0x7a, 0x12,
|
||||
0x1d, 0x99, 0x5b, 0x4a, 0x74, 0x98, 0x97, 0xfd, 0xd1, 0xa8, 0x4b, 0xdc,
|
||||
0xb2, 0x16, 0x2d, 0x19, 0xb3, 0x99, 0x68, 0xc6, 0x4c, 0xbb, 0x65, 0x66,
|
||||
0xb8, 0x27, 0x3b, 0xdc, 0x2d, 0x52, 0xf9, 0x5f, 0xe0, 0xff, 0x61, 0x05,
|
||||
0xb9, 0x4b, 0x0e, 0x10, 0x35, 0x31, 0x0b, 0x5a, 0x05, 0x52, 0x0e, 0xfd,
|
||||
0x7d, 0xdc, 0x75, 0x06, 0x80, 0xd6, 0x2a, 0xbd, 0xc0, 0x65, 0x47, 0x54,
|
||||
0xc7, 0x97, 0xd4, 0xe7, 0x02, 0x77, 0x22, 0x8a, 0xab, 0x91, 0xb9, 0x21,
|
||||
0x01, 0xec, 0xfc, 0x30, 0x4e, 0xdb, 0x31, 0xcf, 0x65, 0xcd, 0xe2, 0x4f,
|
||||
0xc1, 0xab, 0x34, 0xbf, 0xe0, 0xef, 0xa7, 0x5f, 0xde, 0x5e, 0xac, 0x4f,
|
||||
0xde, 0xb9, 0xd5, 0xdf, 0x8b, 0xbf, 0x65, 0xc3, 0x7f, 0x4c, 0xd6, 0xaf,
|
||||
0xe3, 0xfd, 0x2f, 0xaf, 0xdf, 0xe7, 0x5f, 0xde, 0xb1, 0xe2, 0x53, 0xf8,
|
||||
0x32, 0x6d, 0x3a, 0x08, 0x1a, 0x70, 0x5b, 0x34, 0x9c, 0x26, 0xcd, 0x98,
|
||||
0x2e, 0xe4, 0x98, 0x1d, 0x8b, 0x80, 0x20, 0x58, 0xe2, 0xc3, 0xae, 0x6f,
|
||||
0xbe, 0xb8, 0xfd, 0x94, 0xfe, 0x46, 0x9f, 0xa1, 0x9a, 0x0f, 0x9b, 0x00,
|
||||
0x69, 0xae, 0xc4, 0x99, 0xf6, 0xe2, 0x00, 0x70, 0xae, 0x68, 0xd7, 0x8b,
|
||||
0xac, 0x49, 0x1a, 0x33, 0x23, 0x32, 0x10, 0x7f, 0x28, 0xde, 0x1e, 0x5f,
|
||||
0xd0, 0x4f, 0x78, 0x4f, 0xa4, 0xdf, 0xb3, 0x15, 0xd9, 0xdc, 0x81, 0xcc,
|
||||
0xab, 0x49, 0x58, 0xde, 0xa8, 0x73, 0xe9, 0xd0, 0x64, 0xfb, 0xb3, 0x93,
|
||||
0x66, 0xf7, 0xe3, 0x80, 0x5f, 0xf3, 0x33, 0x18, 0xa1, 0xb1, 0xe0, 0xe8,
|
||||
0xe1, 0xb9, 0x6f, 0x8e, 0x3c, 0xa8, 0xb9, 0x63, 0x5f, 0x61, 0xc1, 0xa2,
|
||||
0xf5, 0x68, 0xf2, 0xe8, 0xb0, 0x20, 0x0f, 0xf3, 0xd8, 0x02, 0xfb, 0x7c,
|
||||
0xb0, 0x9a, 0xcf, 0x64, 0xe0, 0xf1, 0xe5, 0x83, 0x39, 0x58, 0x16, 0x86,
|
||||
0x23, 0x0e, 0x3d, 0xad, 0x56, 0xf0, 0xb9, 0x52, 0xe5, 0xb6, 0xf8, 0x0f,
|
||||
0x50, 0xdd, 0x7d, 0xcc, 0xa9, 0x11, 0x00, 0x00
|
||||
};
|
||||
const uint8_t Antonio_SemiBold20pt7bBitmaps[] PROGMEM = {
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7B, 0xDE, 0xF7,
|
||||
0x98, 0xC6, 0x00, 0x03, 0x8F, 0x01, 0xC7, 0x80, 0xE3, 0x80, 0xF1, 0xC0,
|
||||
0x70, 0xE0, 0x38, 0x70, 0x1C, 0x78, 0x0E, 0x3C, 0x07, 0x1C, 0x07, 0x8E,
|
||||
0x03, 0x87, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x38, 0xE0, 0x3C,
|
||||
0x70, 0x1E, 0x38, 0x0E, 0x1C, 0x07, 0x0E, 0x03, 0x8F, 0x0F, 0xFF, 0xF7,
|
||||
0xFF, 0xF8, 0xF1, 0xC0, 0x70, 0xE0, 0x38, 0x70, 0x1C, 0x78, 0x0E, 0x3C,
|
||||
0x0F, 0x1C, 0x07, 0x8E, 0x03, 0x87, 0x01, 0xC3, 0x80, 0xE3, 0xC0, 0x71,
|
||||
0xE0, 0x78, 0xE0, 0x00, 0x06, 0x00, 0x30, 0x01, 0x80, 0x0C, 0x00, 0x60,
|
||||
0x1F, 0xC1, 0xFF, 0x9F, 0xFC, 0xFB, 0xF7, 0x87, 0xBC, 0x3D, 0xE1, 0xEF,
|
||||
0x0F, 0x78, 0x7B, 0xE3, 0xDF, 0x00, 0x7C, 0x01, 0xF8, 0x0F, 0xE0, 0x3F,
|
||||
0x80, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x3E, 0xF0, 0xF7, 0x87, 0xFC, 0x3F,
|
||||
0xE1, 0xFF, 0x0F, 0xFC, 0x7B, 0xFF, 0xCF, 0xFE, 0x3F, 0xE0, 0xFE, 0x01,
|
||||
0xC0, 0x0E, 0x00, 0x70, 0x03, 0x80, 0x1C, 0x00, 0x1F, 0x80, 0x01, 0xC0,
|
||||
0x07, 0xFE, 0x00, 0x3C, 0x00, 0x7F, 0xE0, 0x03, 0xC0, 0x0F, 0x9F, 0x00,
|
||||
0x38, 0x00, 0xF0, 0xF0, 0x07, 0x80, 0x0F, 0x0F, 0x00, 0x70, 0x00, 0xF0,
|
||||
0xF0, 0x0F, 0x00, 0x0F, 0x0F, 0x00, 0xF0, 0x00, 0xF0, 0xF0, 0x0E, 0x00,
|
||||
0x0F, 0x0F, 0x01, 0xE0, 0x00, 0xF0, 0xF0, 0x1C, 0x00, 0x0F, 0x0F, 0x03,
|
||||
0xC0, 0x60, 0xF0, 0xF0, 0x3C, 0x3F, 0xCF, 0x0F, 0x03, 0x87, 0xFE, 0xF0,
|
||||
0xF0, 0x78, 0xFF, 0xEF, 0x0F, 0x07, 0x8F, 0x0E, 0xF0, 0xF0, 0xF0, 0xF0,
|
||||
0xFF, 0x0F, 0x0F, 0x0E, 0x0F, 0xF0, 0xF0, 0xE0, 0xE0, 0xF7, 0xDF, 0x1E,
|
||||
0x0E, 0x0F, 0x7F, 0xE1, 0xE0, 0xE0, 0xF3, 0xFC, 0x1C, 0x0E, 0x0F, 0x1F,
|
||||
0x83, 0xC0, 0xE0, 0xF0, 0x00, 0x38, 0x0E, 0x0F, 0x00, 0x07, 0x80, 0xE0,
|
||||
0xF0, 0x00, 0x78, 0x0E, 0x0F, 0x00, 0x07, 0x00, 0xE0, 0xF0, 0x00, 0xF0,
|
||||
0x0E, 0x0F, 0x00, 0x0E, 0x00, 0xE0, 0xF0, 0x01, 0xE0, 0x0F, 0x0F, 0x00,
|
||||
0x1E, 0x00, 0xF1, 0xE0, 0x01, 0xC0, 0x0F, 0xFE, 0x00, 0x3C, 0x00, 0x7F,
|
||||
0xC0, 0x03, 0x80, 0x03, 0xF8, 0x03, 0xE0, 0x03, 0xFE, 0x00, 0xFF, 0x80,
|
||||
0x7C, 0xF0, 0x1E, 0x1C, 0x07, 0x87, 0x01, 0xE1, 0xC0, 0x78, 0x70, 0x1E,
|
||||
0x1C, 0x07, 0x8F, 0x00, 0xF3, 0x80, 0x3D, 0xE0, 0x0F, 0x78, 0x01, 0xFC,
|
||||
0x00, 0x7E, 0x00, 0x1F, 0x80, 0x07, 0xC0, 0x03, 0xF0, 0x00, 0xFE, 0x08,
|
||||
0x7F, 0x86, 0x3F, 0xE3, 0x8F, 0x3C, 0xF7, 0x8F, 0x79, 0xE1, 0xFC, 0x78,
|
||||
0x7F, 0x3C, 0x1F, 0x8F, 0x03, 0xE3, 0xC0, 0xF0, 0xF0, 0x3E, 0x1E, 0x1F,
|
||||
0x87, 0xFF, 0xF1, 0xFF, 0xFC, 0x3F, 0xE7, 0x87, 0xF1, 0xE0, 0xFF, 0xFF,
|
||||
0xF7, 0x76, 0x66, 0x66, 0x3E, 0xFF, 0xFF, 0xCF, 0x1E, 0x3C, 0x78, 0xF1,
|
||||
0xE3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x3C,
|
||||
0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xFB, 0xF7,
|
||||
0xE1, 0xC0, 0xF1, 0xF3, 0xF1, 0xE3, 0xC7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9,
|
||||
0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E,
|
||||
0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7B, 0xF7, 0xEF, 0x98, 0x00,
|
||||
0x07, 0x00, 0x1C, 0x04, 0x71, 0x39, 0xCE, 0xFB, 0x7D, 0xFF, 0xC1, 0xFC,
|
||||
0x03, 0xE0, 0x3F, 0xE3, 0xEF, 0xFF, 0x73, 0x99, 0xC6, 0x07, 0x08, 0x1C,
|
||||
0x00, 0x70, 0x00, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xF0, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0xFF, 0xFF, 0xFF,
|
||||
0x8C, 0xE7, 0x31, 0x9C, 0xC0, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0x00,
|
||||
0xF8, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0E, 0x00, 0xF0, 0x07, 0x80, 0x3C,
|
||||
0x01, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x38, 0x03, 0xC0, 0x1E, 0x00,
|
||||
0xF0, 0x07, 0x80, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x0F, 0x00, 0x78,
|
||||
0x03, 0xC0, 0x1E, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x3C, 0x01,
|
||||
0xE0, 0x0F, 0x00, 0x78, 0x07, 0x80, 0x00, 0x0F, 0xC1, 0xFF, 0x9F, 0xFC,
|
||||
0xFF, 0xF7, 0x8F, 0xFC, 0x3F, 0xE1, 0xFF, 0x0F, 0xF8, 0x7F, 0xC3, 0xFE,
|
||||
0x1F, 0xF0, 0xFF, 0x87, 0xFC, 0x3F, 0xE1, 0xFF, 0x0F, 0xF8, 0x7F, 0xC3,
|
||||
0xFE, 0x1F, 0xF0, 0xFF, 0x87, 0xFC, 0x3F, 0xE1, 0xFF, 0x0F, 0xF8, 0x7F,
|
||||
0xC3, 0xFE, 0x1F, 0xF0, 0xFF, 0x87, 0xBC, 0x7D, 0xF7, 0xEF, 0xFE, 0x3F,
|
||||
0xF0, 0xFF, 0x00, 0x03, 0x83, 0xC3, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F,
|
||||
0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8, 0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xC3,
|
||||
0xE1, 0xF0, 0xF8, 0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8,
|
||||
0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xC0, 0x0F, 0x81, 0xFF, 0x1F, 0xF8, 0xFF,
|
||||
0xEF, 0x8F, 0x7C, 0x7B, 0xC3, 0xDE, 0x1E, 0xF0, 0xFF, 0x87, 0xFC, 0x3F,
|
||||
0xE1, 0xEF, 0x0F, 0x78, 0x78, 0x07, 0xC0, 0x3C, 0x03, 0xE0, 0x1E, 0x01,
|
||||
0xF0, 0x0F, 0x00, 0xF8, 0x07, 0x80, 0x7C, 0x07, 0xC0, 0x3E, 0x03, 0xE0,
|
||||
0x1F, 0x00, 0xF0, 0x07, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0xFE, 0xFF, 0xF7,
|
||||
0xFF, 0x80, 0x0F, 0xC0, 0xFF, 0x87, 0xFF, 0x1F, 0xFE, 0x78, 0xFB, 0xE1,
|
||||
0xEF, 0x87, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1, 0xE0, 0x07, 0x80, 0x1E, 0x00,
|
||||
0x78, 0x03, 0xE0, 0x3F, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0xC0, 0x0F, 0x00,
|
||||
0x3E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0xBC, 0x1E, 0xF0, 0x7F, 0xC1, 0xFF,
|
||||
0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xE1, 0xE7, 0xDF, 0x9F, 0xFC, 0x3F, 0xE0,
|
||||
0x7F, 0x00, 0x01, 0xF0, 0x07, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x1F,
|
||||
0xC0, 0x7F, 0x01, 0xFC, 0x06, 0xF0, 0x3B, 0xC0, 0xEF, 0x03, 0xBC, 0x1C,
|
||||
0xF0, 0x73, 0xC1, 0xCF, 0x0F, 0x3C, 0x38, 0xF0, 0xE3, 0xC7, 0x8F, 0x1C,
|
||||
0x3C, 0x70, 0xF1, 0xC3, 0xCF, 0x0F, 0x38, 0x3C, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0,
|
||||
0x03, 0xC0, 0xFF, 0xEF, 0xFE, 0xFF, 0xEF, 0xFE, 0xF0, 0x0F, 0x00, 0xF0,
|
||||
0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF3, 0x0F, 0xFC, 0xFF, 0xEF, 0xFE, 0xF9,
|
||||
0xEF, 0x1F, 0xF0, 0xF1, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00,
|
||||
0xF0, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x1F, 0xFB,
|
||||
0xEF, 0xFE, 0x7F, 0xC3, 0xF8, 0x0F, 0xC0, 0xFF, 0x83, 0xFF, 0x1F, 0xFC,
|
||||
0x78, 0xF9, 0xE1, 0xEF, 0x87, 0xBE, 0x1E, 0xF8, 0x7B, 0xE1, 0xEF, 0x80,
|
||||
0x3E, 0x00, 0xF8, 0x03, 0xEF, 0x8F, 0xFF, 0x3F, 0xFE, 0xFF, 0xFB, 0xE1,
|
||||
0xEF, 0x87, 0xBE, 0x1E, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8,
|
||||
0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x79, 0xE1, 0xE7, 0xFF, 0x9F,
|
||||
0xFE, 0x3F, 0xF0, 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x7C, 0x01, 0xE0, 0x0F, 0x80, 0x3C, 0x00, 0xF0, 0x07, 0xC0, 0x1E,
|
||||
0x00, 0x78, 0x03, 0xE0, 0x0F, 0x80, 0x3C, 0x00, 0xF0, 0x07, 0xC0, 0x1E,
|
||||
0x00, 0x78, 0x03, 0xE0, 0x0F, 0x80, 0x3C, 0x00, 0xF0, 0x07, 0xC0, 0x1F,
|
||||
0x00, 0x7C, 0x01, 0xF0, 0x07, 0x80, 0x1E, 0x00, 0xF8, 0x03, 0xE0, 0x0F,
|
||||
0x80, 0x3E, 0x00, 0xF8, 0x00, 0x1F, 0xC1, 0xFF, 0x1F, 0xFC, 0xFB, 0xFF,
|
||||
0x8F, 0xFC, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0,
|
||||
0xFF, 0x87, 0xBC, 0x79, 0xFF, 0xC7, 0xFC, 0x3F, 0xE3, 0xFF, 0x9E, 0x3D,
|
||||
0xF1, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC,
|
||||
0x1F, 0xE0, 0xFF, 0x07, 0xFC, 0x3F, 0xF7, 0xEF, 0xFE, 0x3F, 0xF0, 0xFE,
|
||||
0x00, 0x1F, 0x81, 0xFF, 0x1F, 0xFD, 0xFF, 0xEF, 0x8F, 0x78, 0x7F, 0xC3,
|
||||
0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F,
|
||||
0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0xC7, 0xDF, 0xFE, 0xFF, 0xF3, 0xFF, 0x80,
|
||||
0x3C, 0x01, 0xE0, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x0F,
|
||||
0xFC, 0x79, 0xF7, 0xCF, 0xFE, 0x3F, 0xE0, 0xFE, 0x00, 0xFF, 0xFF, 0xF0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xF3, 0x9C, 0xCE, 0x73, 0x98,
|
||||
0x00, 0x20, 0x0C, 0x07, 0x81, 0xF0, 0xFE, 0x3F, 0x9F, 0xC7, 0xF0, 0xF8,
|
||||
0x1C, 0x03, 0xE0, 0x7E, 0x07, 0xF0, 0x3F, 0x03, 0xF8, 0x1F, 0x01, 0xE0,
|
||||
0x0C, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x0F, 0xFF,
|
||||
0xFF, 0xFF, 0xC0, 0x80, 0x18, 0x03, 0x80, 0x7C, 0x0F, 0xC0, 0xFE, 0x07,
|
||||
0xF0, 0x7F, 0x03, 0xE0, 0x3C, 0x0F, 0x83, 0xF1, 0xFC, 0xFE, 0x3F, 0x87,
|
||||
0xC0, 0xF0, 0x18, 0x02, 0x00, 0x00, 0x1F, 0x87, 0xFC, 0x7F, 0xEF, 0xFE,
|
||||
0xF1, 0xEF, 0x1F, 0xF1, 0xFF, 0x1F, 0xF1, 0xFF, 0x1F, 0xF1, 0xFF, 0x1F,
|
||||
0x01, 0xF0, 0x1E, 0x01, 0xE0, 0x3E, 0x03, 0xE0, 0xFC, 0x3F, 0x83, 0xF0,
|
||||
0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x00, 0x3F, 0x80,
|
||||
0x00, 0xFF, 0xF0, 0x00, 0xFF, 0xFC, 0x00, 0xF8, 0x0F, 0x00, 0xF0, 0x03,
|
||||
0xC0, 0xF0, 0x00, 0xF0, 0xF0, 0x00, 0x38, 0x78, 0x00, 0x1C, 0x38, 0x1F,
|
||||
0x87, 0x3C, 0x3F, 0xE3, 0x9E, 0x1E, 0xF1, 0xCE, 0x1C, 0x38, 0xE7, 0x0E,
|
||||
0x1C, 0x77, 0x8E, 0x0E, 0x1F, 0xC7, 0x07, 0x0F, 0xE3, 0x83, 0x87, 0xF1,
|
||||
0xC1, 0xC3, 0xF8, 0xE0, 0xE1, 0xFC, 0x70, 0x70, 0xFE, 0x38, 0x38, 0x7F,
|
||||
0x1C, 0x1C, 0x77, 0x8E, 0x0E, 0x39, 0xC7, 0x07, 0x1C, 0xE3, 0xC7, 0xCE,
|
||||
0x70, 0xF6, 0xFE, 0x3C, 0x7F, 0x3F, 0x1E, 0x1F, 0x0F, 0x07, 0x81, 0x00,
|
||||
0x03, 0xC0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x1F, 0x80,
|
||||
0x70, 0x07, 0xFF, 0xF8, 0x00, 0xFF, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0x07,
|
||||
0xC0, 0x07, 0xE0, 0x0F, 0xE0, 0x0F, 0xE0, 0x0F, 0xE0, 0x0F, 0xE0, 0x0F,
|
||||
0xE0, 0x0F, 0xF0, 0x0E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E,
|
||||
0xF0, 0x1E, 0x70, 0x1E, 0x78, 0x1C, 0x78, 0x3C, 0x78, 0x3C, 0x78, 0x3C,
|
||||
0x78, 0x3C, 0x78, 0x3C, 0x3C, 0x3C, 0x3C, 0x7C, 0x3C, 0x78, 0x3C, 0x7F,
|
||||
0xFC, 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFE, 0x78, 0x1E, 0xF8, 0x1E, 0xF8,
|
||||
0x1E, 0xF0, 0x1E, 0xF0, 0x1F, 0xF0, 0x1F, 0xFF, 0x03, 0xFF, 0x8F, 0xFF,
|
||||
0x3F, 0xFC, 0xF0, 0xFB, 0xC3, 0xEF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1,
|
||||
0xEF, 0x07, 0xBC, 0x3E, 0xF0, 0xFB, 0xC3, 0xCF, 0xFF, 0x3F, 0xF8, 0xFF,
|
||||
0xF3, 0xFF, 0xCF, 0x0F, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1, 0xEF, 0x07, 0xFC,
|
||||
0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC3, 0xEF,
|
||||
0xFF, 0xBF, 0xFC, 0xFF, 0xE3, 0xFF, 0x00, 0x0F, 0xC0, 0x7F, 0xC3, 0xFF,
|
||||
0x9F, 0xFE, 0x7C, 0x79, 0xE1, 0xFF, 0x87, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0,
|
||||
0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8,
|
||||
0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE,
|
||||
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x1F, 0x78, 0x7D, 0xE1, 0xE7,
|
||||
0xEF, 0x8F, 0xFE, 0x3F, 0xF0, 0x3F, 0x80, 0xFF, 0x07, 0xFF, 0x3F, 0xFD,
|
||||
0xFF, 0xEF, 0x0F, 0xF8, 0x7F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC,
|
||||
0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83,
|
||||
0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F,
|
||||
0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x7F, 0xFF, 0xDF, 0xFE, 0xFF,
|
||||
0xE7, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3C, 0x0F, 0x03,
|
||||
0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F,
|
||||
0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03,
|
||||
0xC0, 0xF0, 0x3C, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0xF0,
|
||||
0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F,
|
||||
0x03, 0xC0, 0xF0, 0x3C, 0x00, 0x0F, 0xE0, 0x7F, 0xC3, 0xFF, 0x9F, 0xFE,
|
||||
0x7C, 0x7D, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83,
|
||||
0xFE, 0x0F, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF9, 0xFF, 0xE7,
|
||||
0xFF, 0x9F, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8,
|
||||
0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0x78, 0x3D, 0xE1, 0xF7, 0xEF, 0xCF,
|
||||
0xFF, 0x3F, 0xEC, 0x3F, 0x30, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F,
|
||||
0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07,
|
||||
0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0,
|
||||
0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC,
|
||||
0x1F, 0xF0, 0x7F, 0xC1, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF8,
|
||||
0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0x00,
|
||||
0xF8, 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F,
|
||||
0x00, 0xF8, 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0xF8, 0x7F, 0xC3, 0xFE,
|
||||
0x1F, 0xF0, 0xFF, 0x87, 0xFC, 0x3F, 0xE1, 0xEF, 0x0F, 0x78, 0x7B, 0xF7,
|
||||
0xCF, 0xFC, 0x7F, 0xE1, 0xFE, 0x00, 0xF0, 0x3D, 0xE0, 0xFB, 0xC1, 0xE7,
|
||||
0x83, 0xCF, 0x0F, 0x9E, 0x1E, 0x3C, 0x3C, 0x78, 0xF8, 0xF1, 0xE1, 0xE7,
|
||||
0xC3, 0xCF, 0x87, 0x9E, 0x0F, 0x7C, 0x1E, 0xF8, 0x3D, 0xE0, 0x7F, 0xC0,
|
||||
0xFF, 0x01, 0xFE, 0x03, 0xFE, 0x07, 0xBC, 0x0F, 0x7C, 0x1E, 0xF8, 0x3C,
|
||||
0xF0, 0x79, 0xF0, 0xF1, 0xE1, 0xE3, 0xC3, 0xC7, 0xC7, 0x87, 0x8F, 0x0F,
|
||||
0x9E, 0x1F, 0x3C, 0x1E, 0x78, 0x3E, 0xF0, 0x7D, 0xE0, 0x7C, 0xF0, 0x3C,
|
||||
0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03,
|
||||
0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0,
|
||||
0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x03,
|
||||
0xFF, 0xC0, 0x3F, 0xFC, 0x03, 0xFF, 0xC0, 0x3F, 0xFC, 0x03, 0xFF, 0xC0,
|
||||
0x7F, 0xFE, 0x07, 0xFF, 0xE0, 0x7F, 0xFE, 0x07, 0xFE, 0xE0, 0x7F, 0xEE,
|
||||
0x0F, 0xFE, 0xF0, 0xFF, 0xEF, 0x0E, 0xFE, 0x70, 0xEF, 0xE7, 0x0E, 0xFE,
|
||||
0x70, 0xEF, 0xE7, 0x1E, 0xFE, 0x79, 0xCF, 0xE3, 0x9C, 0xFF, 0x39, 0xCF,
|
||||
0xF3, 0x9C, 0xFF, 0x3B, 0xCF, 0xF3, 0xF8, 0xFF, 0x1F, 0x8F, 0xF1, 0xF8,
|
||||
0xFF, 0x1F, 0x8F, 0xF1, 0xF8, 0xFF, 0x1F, 0x8F, 0xF0, 0xF0, 0xFF, 0x0F,
|
||||
0x0F, 0xF0, 0xF0, 0xFF, 0x0F, 0x0F, 0xE0, 0x3F, 0x80, 0xFF, 0x03, 0xFC,
|
||||
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFF, 0x0F, 0xFC, 0x3F, 0xF0, 0xFF,
|
||||
0xE3, 0xFF, 0x8F, 0xFE, 0x3F, 0xFC, 0xFF, 0xF3, 0xFD, 0xCF, 0xF7, 0xBF,
|
||||
0xDE, 0xFF, 0x3B, 0xFC, 0xFF, 0xF3, 0xDF, 0xC7, 0x7F, 0x1F, 0xFC, 0x7F,
|
||||
0xF0, 0xFF, 0xC3, 0xFF, 0x0F, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x03,
|
||||
0xFC, 0x0F, 0xF0, 0x3F, 0xC0, 0x70, 0x0F, 0xE0, 0x7F, 0xC3, 0xFF, 0x9F,
|
||||
0xFE, 0x7C, 0x7D, 0xE1, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF,
|
||||
0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F,
|
||||
0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F,
|
||||
0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0x78, 0x7D, 0xE1, 0xF7, 0xFF,
|
||||
0x8F, 0xFE, 0x3F, 0xF0, 0x3F, 0x80, 0xFF, 0x87, 0xFF, 0x3F, 0xFD, 0xFF,
|
||||
0xEF, 0x0F, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F,
|
||||
0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x1F, 0xF0, 0xFF, 0xFF, 0xBF,
|
||||
0xFD, 0xFF, 0xCF, 0xF8, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80,
|
||||
0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07,
|
||||
0x80, 0x00, 0x0F, 0xE0, 0x7F, 0xC3, 0xFF, 0x9F, 0xFE, 0x7C, 0x7D, 0xE1,
|
||||
0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8,
|
||||
0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE,
|
||||
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF,
|
||||
0x83, 0xFE, 0x0F, 0x78, 0x7D, 0xE1, 0xF7, 0xFF, 0x8F, 0xFE, 0x3F, 0xF0,
|
||||
0x3F, 0x80, 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x01, 0xE0, 0x03, 0x00, 0xFF,
|
||||
0x83, 0xFF, 0x8F, 0xFF, 0x3F, 0xFE, 0xF0, 0xFB, 0xC1, 0xEF, 0x07, 0xBC,
|
||||
0x1E, 0xF0, 0x7B, 0xC1, 0xEF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC3, 0xEF,
|
||||
0x1F, 0x3F, 0xFC, 0xFF, 0xE3, 0xFF, 0xCF, 0x0F, 0xBC, 0x1E, 0xF0, 0x7B,
|
||||
0xC1, 0xEF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1, 0xEF, 0x07, 0xBC, 0x1E,
|
||||
0xF0, 0x7B, 0xC1, 0xEF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1, 0xF0, 0x0F,
|
||||
0xC0, 0xFF, 0xC3, 0xFF, 0x1F, 0xFE, 0x78, 0x79, 0xE1, 0xEF, 0x87, 0xBE,
|
||||
0x1F, 0xF8, 0x7D, 0xE1, 0xF7, 0x87, 0xDF, 0x1F, 0x7E, 0x00, 0xFC, 0x01,
|
||||
0xF8, 0x03, 0xF0, 0x0F, 0xE0, 0x1F, 0x80, 0x3F, 0x00, 0x7E, 0x00, 0xF9,
|
||||
0xE1, 0xF7, 0x87, 0xDE, 0x0F, 0x78, 0x3D, 0xE0, 0xF7, 0x83, 0xDE, 0x0F,
|
||||
0x78, 0x3D, 0xE1, 0xF7, 0xEF, 0x8F, 0xFE, 0x3F, 0xF0, 0x3F, 0x80, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F,
|
||||
0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F,
|
||||
0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F,
|
||||
0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F,
|
||||
0x80, 0xF8, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0,
|
||||
0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8,
|
||||
0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE,
|
||||
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF,
|
||||
0x83, 0xFE, 0x0F, 0x78, 0x3D, 0xE1, 0xF7, 0xEF, 0xCF, 0xFE, 0x3F, 0xF8,
|
||||
0x7F, 0x80, 0xF0, 0x1F, 0xF0, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF8, 0x1E,
|
||||
0x78, 0x1E, 0x78, 0x1E, 0x78, 0x3C, 0x78, 0x3C, 0x78, 0x3C, 0x7C, 0x3C,
|
||||
0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x78, 0x3C, 0x78, 0x3C, 0x78,
|
||||
0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x70, 0x1E, 0xF0,
|
||||
0x0E, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xE0,
|
||||
0x0F, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0xF0, 0x3C,
|
||||
0x0F, 0xF0, 0x3C, 0x0F, 0xF0, 0x3C, 0x0F, 0xF0, 0x3C, 0x1E, 0xF0, 0x3C,
|
||||
0x1E, 0x78, 0x3E, 0x1E, 0x78, 0x7E, 0x1E, 0x78, 0x7E, 0x1E, 0x78, 0x7E,
|
||||
0x1E, 0x78, 0x7E, 0x1E, 0x78, 0x7E, 0x1E, 0x78, 0x7E, 0x1E, 0x38, 0x7F,
|
||||
0x1C, 0x38, 0xF7, 0x1C, 0x3C, 0xF7, 0x3C, 0x3C, 0xE7, 0x3C, 0x3C, 0xE7,
|
||||
0x3C, 0x3C, 0xE7, 0x3C, 0x3C, 0xE7, 0x3C, 0x3C, 0xE7, 0xBC, 0x1D, 0xE3,
|
||||
0xB8, 0x1D, 0xC3, 0xB8, 0x1D, 0xC3, 0xB8, 0x1F, 0xC3, 0xB8, 0x1F, 0xC3,
|
||||
0xB8, 0x1F, 0xC3, 0xF8, 0x1F, 0xC3, 0xF8, 0x1F, 0xC1, 0xF8, 0x0F, 0x81,
|
||||
0xF8, 0x0F, 0x81, 0xF0, 0x0F, 0x81, 0xF0, 0x0F, 0x81, 0xF0, 0x0F, 0x81,
|
||||
0xF0, 0x0F, 0x81, 0xF0, 0xF0, 0x3F, 0xC0, 0xF7, 0x07, 0x9E, 0x1E, 0x78,
|
||||
0x78, 0xE3, 0xE3, 0xCF, 0x0F, 0x3C, 0x3C, 0xF0, 0x7F, 0x81, 0xFE, 0x07,
|
||||
0xF8, 0x0F, 0xE0, 0x3F, 0x00, 0xFC, 0x01, 0xF0, 0x07, 0x80, 0x3E, 0x00,
|
||||
0xF8, 0x03, 0xF0, 0x0F, 0xC0, 0x7F, 0x01, 0xFE, 0x07, 0xF8, 0x3F, 0xE0,
|
||||
0xF3, 0xC3, 0xCF, 0x0F, 0x3C, 0x78, 0x71, 0xE1, 0xE7, 0x87, 0x9E, 0x0E,
|
||||
0xF0, 0x3F, 0xC0, 0xF0, 0xF8, 0x0F, 0x78, 0x1F, 0x78, 0x1E, 0x7C, 0x1E,
|
||||
0x3C, 0x1E, 0x3C, 0x3E, 0x3C, 0x3C, 0x3E, 0x3C, 0x1E, 0x3C, 0x1E, 0x78,
|
||||
0x1E, 0x78, 0x0F, 0x78, 0x0F, 0x78, 0x0F, 0xF0, 0x0F, 0xF0, 0x07, 0xF0,
|
||||
0x07, 0xF0, 0x07, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0,
|
||||
0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0,
|
||||
0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0,
|
||||
0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF, 0x01, 0xF0, 0x1E, 0x01, 0xE0, 0x3E,
|
||||
0x03, 0xE0, 0x3C, 0x07, 0xC0, 0x7C, 0x07, 0xC0, 0x78, 0x0F, 0x80, 0xF8,
|
||||
0x0F, 0x80, 0xF0, 0x1F, 0x01, 0xF0, 0x1E, 0x01, 0xE0, 0x3E, 0x03, 0xE0,
|
||||
0x3C, 0x07, 0xC0, 0x7C, 0x07, 0xC0, 0x78, 0x0F, 0x80, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
|
||||
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
|
||||
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF,
|
||||
0xFF, 0xFF, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x07, 0x00, 0xF0, 0x1E, 0x03,
|
||||
0xC0, 0x78, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x3C, 0x07, 0x80, 0xF0,
|
||||
0x1E, 0x01, 0xC0, 0x3C, 0x07, 0x80, 0xF0, 0x0E, 0x01, 0xE0, 0x3C, 0x07,
|
||||
0x80, 0xF0, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0x78, 0x0F, 0x01, 0xE0,
|
||||
0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
|
||||
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
|
||||
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF,
|
||||
0x0F, 0xC0, 0x1F, 0x80, 0x3F, 0x80, 0x7F, 0x01, 0xFE, 0x03, 0xDC, 0x07,
|
||||
0xBC, 0x0E, 0x78, 0x3C, 0xF0, 0x79, 0xE0, 0xF1, 0xE1, 0xE3, 0xC7, 0x87,
|
||||
0x8F, 0x0F, 0x1E, 0x0F, 0x3C, 0x1E, 0xF8, 0x3D, 0xE0, 0x7C, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xF0, 0xF9, 0xE7, 0x8E, 0x38, 0x71, 0xC3, 0x0F, 0xC0, 0xFF,
|
||||
0xC3, 0xFF, 0x1F, 0xFE, 0x78, 0x79, 0xE1, 0xEF, 0x87, 0xBE, 0x1E, 0xF8,
|
||||
0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x7F, 0x0F, 0xFC, 0x7F, 0xF3, 0xE7, 0xDE,
|
||||
0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF,
|
||||
0x87, 0xFE, 0x1F, 0xF8, 0x7D, 0xE3, 0xF7, 0xFF, 0xDF, 0xFF, 0x3F, 0x7C,
|
||||
0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x79, 0xE3, 0xDF, 0x9F,
|
||||
0xFE, 0xFF, 0xFF, 0xC7, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1,
|
||||
0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F,
|
||||
0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xFC,
|
||||
0x7F, 0xF7, 0xFF, 0xFE, 0xF7, 0xF7, 0x9F, 0x00, 0x0F, 0xC1, 0xFF, 0x8F,
|
||||
0xFC, 0xFF, 0xF7, 0x87, 0xBC, 0x3F, 0xE1, 0xFF, 0x0F, 0xF8, 0x7F, 0xC3,
|
||||
0xFE, 0x1F, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x07,
|
||||
0xC0, 0x3E, 0x1F, 0xF0, 0xFF, 0x87, 0xFC, 0x3F, 0xE1, 0xEF, 0x0F, 0x78,
|
||||
0x7B, 0xE7, 0xDF, 0xFC, 0x7F, 0xE1, 0xFE, 0x00, 0x00, 0x7C, 0x01, 0xF0,
|
||||
0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x7D, 0xF3, 0xFF, 0xDF, 0xFF, 0x7F, 0xFD,
|
||||
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F,
|
||||
0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87,
|
||||
0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7D, 0xE1,
|
||||
0xF7, 0xDF, 0xDF, 0xFF, 0x3F, 0xFC, 0x7D, 0xF0, 0x0F, 0xC0, 0x7F, 0xC3,
|
||||
0xFF, 0x1F, 0xFE, 0x78, 0x79, 0xE1, 0xEF, 0x87, 0xBE, 0x1E, 0xF8, 0x7B,
|
||||
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00,
|
||||
0xF8, 0x03, 0xE0, 0x0F, 0x87, 0xBE, 0x1E, 0xF8, 0x7B, 0xE1, 0xEF, 0x87,
|
||||
0x9E, 0x1E, 0x78, 0x79, 0xF3, 0xE7, 0xFF, 0x0F, 0xFC, 0x1F, 0xE0, 0x07,
|
||||
0xC3, 0xF1, 0xFC, 0x7C, 0x1E, 0x07, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1,
|
||||
0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78,
|
||||
0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07,
|
||||
0x81, 0xE0, 0x78, 0x1E, 0x07, 0x80, 0x1F, 0x7C, 0xFF, 0xF7, 0xFF, 0xDF,
|
||||
0xFF, 0x78, 0x7D, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF,
|
||||
0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F,
|
||||
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7D, 0xE1, 0xF7, 0xCF, 0xDF, 0xFF,
|
||||
0x3F, 0xFC, 0x7D, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x79, 0xC3, 0xE7, 0xFF,
|
||||
0xBF, 0xFC, 0x3F, 0xC0, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00,
|
||||
0x79, 0xF3, 0xDF, 0xDF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFC, 0x1F, 0xE0, 0xFF,
|
||||
0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0,
|
||||
0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F,
|
||||
0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xC0,
|
||||
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3C, 0x78, 0xF1, 0xE0, 0x00, 0x0F,
|
||||
0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3,
|
||||
0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C,
|
||||
0xF9, 0xFF, 0xDF, 0xBE, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00,
|
||||
0xF0, 0x03, 0xC1, 0xFF, 0x07, 0xBC, 0x1E, 0xF0, 0xFB, 0xC3, 0xCF, 0x1F,
|
||||
0x3C, 0x78, 0xF1, 0xE3, 0xCF, 0x8F, 0x3C, 0x3D, 0xF0, 0xF7, 0x83, 0xDE,
|
||||
0x0F, 0xF8, 0x3F, 0xC0, 0xFF, 0x83, 0xDE, 0x0F, 0x78, 0x3D, 0xF0, 0xF3,
|
||||
0xC3, 0xCF, 0x8F, 0x1E, 0x3C, 0x7C, 0xF0, 0xF3, 0xC3, 0xCF, 0x0F, 0xBC,
|
||||
0x1E, 0xF0, 0x7F, 0xC0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xE1,
|
||||
0xF3, 0xDF, 0xCF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFC, 0x3F,
|
||||
0xC1, 0xE0, 0xFF, 0x07, 0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1,
|
||||
0xE0, 0xFF, 0x07, 0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1, 0xE0,
|
||||
0xFF, 0x07, 0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1, 0xE0, 0xFF,
|
||||
0x07, 0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1, 0xE0, 0xFF, 0x07,
|
||||
0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1, 0xE0, 0xFF, 0x07, 0x83,
|
||||
0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3C, 0xF3, 0xE7, 0xBF, 0xBF, 0xFF, 0xFF,
|
||||
0xFF, 0x87, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F,
|
||||
0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC,
|
||||
0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83,
|
||||
0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0x80, 0x0F, 0xC0, 0x7F, 0x83, 0xFF, 0x1F,
|
||||
0xFE, 0x78, 0x79, 0xE1, 0xEF, 0x87, 0xBE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF,
|
||||
0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F,
|
||||
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xDE, 0x1E,
|
||||
0x78, 0x79, 0xF3, 0xE7, 0xFF, 0x0F, 0xFC, 0x1F, 0xE0, 0xF3, 0xC7, 0xBF,
|
||||
0xBF, 0xFD, 0xFF, 0xFF, 0x8F, 0xF8, 0x7F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F,
|
||||
0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0,
|
||||
0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F,
|
||||
0xF8, 0xFF, 0xEF, 0xFF, 0xFD, 0xFF, 0xEF, 0x7E, 0x78, 0x03, 0xC0, 0x1E,
|
||||
0x00, 0xF0, 0x07, 0x80, 0x00, 0x1E, 0x7C, 0xFF, 0xF7, 0xFF, 0xDF, 0xFF,
|
||||
0x78, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87,
|
||||
0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1,
|
||||
0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0x78,
|
||||
0x7D, 0xF7, 0xF7, 0xFF, 0xCF, 0xFF, 0x1F, 0x7C, 0x01, 0xF0, 0x07, 0xC0,
|
||||
0x1F, 0x00, 0x7C, 0x01, 0xF0, 0xF3, 0xFB, 0xFF, 0xFF, 0xFF, 0xC7, 0x83,
|
||||
0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0,
|
||||
0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E,
|
||||
0x0F, 0x00, 0x1F, 0x83, 0xFE, 0x1F, 0xF9, 0xFF, 0xCF, 0x0F, 0x78, 0x7B,
|
||||
0xC3, 0xDE, 0x1E, 0xF0, 0xF7, 0xC7, 0xBE, 0x00, 0xF8, 0x07, 0xE0, 0x1F,
|
||||
0x80, 0x7E, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0x80, 0x7D, 0xE1, 0xEF, 0x0F,
|
||||
0xF8, 0x7F, 0xC3, 0xFE, 0x1F, 0xF0, 0xF7, 0xEF, 0x9F, 0xFC, 0xFF, 0xC1,
|
||||
0xFC, 0x00, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x8F, 0xFF, 0xFF,
|
||||
0xFF, 0xCF, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F,
|
||||
0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0,
|
||||
0xF8, 0x3E, 0x0F, 0x83, 0xF0, 0xFF, 0x1F, 0xC3, 0xF0, 0xF8, 0x3F, 0xE0,
|
||||
0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8,
|
||||
0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE,
|
||||
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF,
|
||||
0x83, 0xFE, 0x0F, 0x78, 0x7D, 0xF3, 0xF7, 0xFF, 0xCF, 0xEF, 0x1F, 0x3C,
|
||||
0xF0, 0x7F, 0xC1, 0xEF, 0x07, 0xBC, 0x1E, 0x78, 0x79, 0xE1, 0xE7, 0x87,
|
||||
0x9E, 0x1C, 0x78, 0x71, 0xE3, 0xC7, 0x8F, 0x0E, 0x3C, 0x3C, 0xF0, 0xF3,
|
||||
0xC3, 0xCE, 0x0F, 0x38, 0x3C, 0xE0, 0xF3, 0x81, 0xDE, 0x07, 0x78, 0x1F,
|
||||
0xE0, 0x7F, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0x3F, 0x00, 0xFC, 0x03,
|
||||
0xF0, 0x0F, 0x80, 0xF0, 0x78, 0x7F, 0x87, 0xC3, 0xFC, 0x3E, 0x1F, 0xE1,
|
||||
0xF0, 0xF7, 0x0F, 0x87, 0xB8, 0x7C, 0x39, 0xC3, 0xE1, 0xCE, 0x1F, 0x8E,
|
||||
0x79, 0xDC, 0x73, 0xCE, 0xE7, 0x9E, 0x77, 0x3C, 0xF3, 0xB9, 0xE3, 0x9D,
|
||||
0xCF, 0x1C, 0xE6, 0x70, 0xE7, 0x3B, 0x87, 0x71, 0xDC, 0x3B, 0x8E, 0xE1,
|
||||
0xDC, 0x77, 0x0F, 0xE3, 0xB8, 0x7F, 0x1D, 0xC3, 0xF8, 0xFE, 0x0F, 0x83,
|
||||
0xE0, 0x7C, 0x1F, 0x03, 0xE0, 0xF8, 0x1F, 0x07, 0xC0, 0xF8, 0x3E, 0x07,
|
||||
0xC1, 0xF0, 0x3E, 0x0F, 0x81, 0xE0, 0x7C, 0x00, 0xF0, 0x7B, 0xC1, 0xE7,
|
||||
0x87, 0x9E, 0x3C, 0x78, 0xF0, 0xF3, 0xC3, 0xCE, 0x0F, 0x78, 0x1F, 0xE0,
|
||||
0x7F, 0x01, 0xFC, 0x03, 0xF0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0,
|
||||
0x0F, 0x80, 0x7F, 0x01, 0xFC, 0x07, 0xF0, 0x3D, 0xE0, 0xF7, 0x83, 0xDE,
|
||||
0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xCF, 0x07, 0xBC, 0x1E, 0xF0, 0x7C, 0xF0,
|
||||
0x3F, 0xC0, 0xFF, 0x07, 0xFE, 0x1E, 0x78, 0x79, 0xE1, 0xE7, 0x87, 0x9E,
|
||||
0x1E, 0x78, 0x79, 0xE1, 0xC3, 0x87, 0x0F, 0x3C, 0x3C, 0xF0, 0xF3, 0xC3,
|
||||
0xCF, 0x0F, 0x3C, 0x1C, 0xE0, 0x73, 0x81, 0xCE, 0x07, 0xB8, 0x1F, 0xE0,
|
||||
0x7F, 0x80, 0xFC, 0x03, 0xF0, 0x0F, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0,
|
||||
0x07, 0x80, 0x1E, 0x00, 0xF8, 0x1F, 0xE0, 0x7F, 0x01, 0xFC, 0x00, 0x7F,
|
||||
0xEF, 0xFD, 0xFF, 0xBF, 0xF0, 0x1E, 0x07, 0xC0, 0xF0, 0x1E, 0x07, 0xC0,
|
||||
0xF0, 0x1E, 0x03, 0xC0, 0xF8, 0x1E, 0x03, 0xC0, 0xF8, 0x1E, 0x03, 0xC0,
|
||||
0xF8, 0x1F, 0x03, 0xC0, 0x78, 0x1F, 0x03, 0xC0, 0x78, 0x1F, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFE, 0x07, 0xC3, 0xF1, 0xFC, 0x7E, 0x1E, 0x07, 0x81, 0xE0,
|
||||
0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0xF8, 0x7E,
|
||||
0x3F, 0x0F, 0x83, 0xF0, 0xFE, 0x0F, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81,
|
||||
0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0xC1, 0xFC, 0x3F,
|
||||
0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF8, 0x3F, 0x0F,
|
||||
0xC1, 0xF8, 0x3E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78,
|
||||
0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1F, 0x83, 0xF0, 0x7C, 0x3F, 0x1F, 0xC7,
|
||||
0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0,
|
||||
0x78, 0x3E, 0x0F, 0x8F, 0xC3, 0xF0, 0xF8, 0x00, 0x1E, 0x07, 0x9F, 0xF3,
|
||||
0xDF, 0xFF, 0xDF, 0xFF, 0xEF, 0xFF, 0xE1, 0x03, 0xE0};
|
||||
|
||||
const GFXglyph Antonio_SemiBold20pt7bGlyphs[] PROGMEM = {
|
||||
{0, 1, 1, 8, 0, 0}, // 0x20 ' '
|
||||
|
@ -284,13 +477,8 @@ const GFXglyph Antonio_SemiBold20pt7bGlyphs[] PROGMEM = {
|
|||
{4461, 10, 37, 14, 2, -33}, // 0x7D '}'
|
||||
{4508, 17, 6, 21, 2, -21}}; // 0x7E '~'
|
||||
|
||||
// Font properties
|
||||
static constexpr FontData Antonio_SemiBold20pt7b_Properties = {
|
||||
Antonio_SemiBold20pt7bBitmaps_Gzip,
|
||||
Antonio_SemiBold20pt7bGlyphs,
|
||||
sizeof(Antonio_SemiBold20pt7bBitmaps_Gzip),
|
||||
4521, // Original size
|
||||
0x20, // First char
|
||||
0x7E, // Last char
|
||||
51 // yAdvance
|
||||
};
|
||||
const GFXfont Antonio_SemiBold20pt7b PROGMEM = {
|
||||
(uint8_t *)Antonio_SemiBold20pt7bBitmaps,
|
||||
(GFXglyph *)Antonio_SemiBold20pt7bGlyphs, 0x20, 0x7E, 51};
|
||||
|
||||
// Approx. 5193 bytes
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,97 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Arduino.h>
|
||||
#include <rom/miniz.h>
|
||||
#include "antonio-semibold20.h"
|
||||
//#include "antonio-semibold30.h"
|
||||
#include "antonio-semibold40.h"
|
||||
#include "antonio-semibold90.h"
|
||||
#include "sats-symbol.h"
|
||||
//#include "icons.h"
|
||||
|
||||
// Font metadata structure
|
||||
struct FontData {
|
||||
const uint8_t* compressedData;
|
||||
const GFXglyph* glyphs;
|
||||
const size_t compressedSize;
|
||||
const size_t originalSize;
|
||||
const uint16_t first;
|
||||
const uint16_t last;
|
||||
const uint8_t yAdvance;
|
||||
};
|
||||
|
||||
// Font name constants
|
||||
namespace FontNames {
|
||||
static const String ANTONIO = "antonio";
|
||||
static const String OSWALD = "oswald";
|
||||
|
||||
static const std::array<String, 2> AVAILABLE_FONTS = {
|
||||
ANTONIO,
|
||||
OSWALD
|
||||
};
|
||||
|
||||
static const std::array<String, 2>& getAvailableFonts() {
|
||||
return AVAILABLE_FONTS;
|
||||
}
|
||||
}
|
||||
|
||||
class FontLoader {
|
||||
public:
|
||||
static GFXfont* loadCompressedFont(const FontData& fontData) {
|
||||
return loadCompressedFont(
|
||||
fontData.compressedData,
|
||||
fontData.glyphs,
|
||||
fontData.compressedSize,
|
||||
fontData.originalSize,
|
||||
fontData.first,
|
||||
fontData.last,
|
||||
fontData.yAdvance
|
||||
);
|
||||
}
|
||||
|
||||
static GFXfont* loadCompressedFont(
|
||||
const uint8_t* compressedData,
|
||||
const GFXglyph* glyphs,
|
||||
const size_t compressedSize,
|
||||
const size_t originalSize,
|
||||
const uint16_t first,
|
||||
const uint16_t last,
|
||||
const uint8_t yAdvance)
|
||||
{
|
||||
uint8_t* decompressedData = (uint8_t*)malloc(originalSize);
|
||||
if (!decompressedData) {
|
||||
Serial.println(F("Failed to allocate memory for font decompression"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t decompressedSize = originalSize;
|
||||
if (GzipDecompressor::decompressData(compressedData,
|
||||
compressedSize,
|
||||
decompressedData,
|
||||
&decompressedSize))
|
||||
{
|
||||
GFXfont* font = (GFXfont*)malloc(sizeof(GFXfont));
|
||||
if (!font) {
|
||||
free(decompressedData);
|
||||
Serial.println(F("Failed to allocate memory for font structure"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
font->bitmap = decompressedData;
|
||||
font->glyph = (GFXglyph*)glyphs;
|
||||
font->first = first;
|
||||
font->last = last;
|
||||
font->yAdvance = yAdvance;
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
Serial.println(F("Font decompression failed"));
|
||||
free(decompressedData);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void unloadFont(GFXfont* font) {
|
||||
if (font) {
|
||||
if (font->bitmap) {
|
||||
free((void*)font->bitmap);
|
||||
}
|
||||
free(font);
|
||||
}
|
||||
}
|
||||
};
|
||||
// #include "oswald-20.h"
|
||||
// #include "oswald-30.h"
|
||||
// #include "oswald-90.h"
|
||||
// #include "ubuntu-italic40.h"
|
||||
// #include "ubuntu-italic60.h"
|
||||
// #include "ubuntu-italic70.h"
|
|
@ -1,323 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Arduino.h>
|
||||
#include "fonts.hpp"
|
||||
|
||||
const uint8_t Oswald_Medium20pt7bBitmaps_Gzip[] = {
|
||||
0x1f,0x8b,0x08,0x00,0xf7,0xa4,0x71,0x67,0x02,0xff,0xad,0x58,
|
||||
0xcf,0x8e,0xdb,0xb8,0x19,0xa7,0xa0,0xa2,0xbc,0x04,0xe1,0xb5,
|
||||
0x87,0x81,0x98,0x37,0xe8,0x1e,0xb3,0x80,0x22,0xf6,0x51,0xda,
|
||||
0x37,0x48,0xb1,0x87,0x3a,0x18,0x45,0xd4,0xc0,0x07,0xdf,0x56,
|
||||
0x2f,0xb0,0x88,0x5f,0x63,0x81,0xa6,0x19,0x1a,0x2e,0xe0,0x4b,
|
||||
0xb1,0x7e,0x81,0x36,0xa6,0xa1,0x83,0x6f,0x2b,0x1a,0x3e,0x98,
|
||||
0x86,0x39,0x64,0x7f,0x94,0xec,0x19,0x4f,0x32,0x49,0x16,0x45,
|
||||
0xc5,0xb1,0xf4,0xf1,0xff,0xc7,0xef,0xef,0x8f,0x43,0x42,0x7c,
|
||||
0x8e,0xff,0xde,0x7c,0xff,0xcb,0x0f,0x3f,0x6d,0xbe,0xff,0xd7,
|
||||
0x8b,0x24,0x56,0xbd,0x5c,0xbe,0xe9,0x5e,0x6d,0x7e,0xde,0x67,
|
||||
0xed,0x4f,0x3f,0xfc,0x48,0x58,0x43,0x27,0xe3,0xc5,0xac,0x55,
|
||||
0x5b,0x3d,0x32,0xf9,0xa8,0xc8,0xb3,0x8c,0x31,0xd1,0xcf,0x34,
|
||||
0x5b,0x6d,0xcd,0x68,0x94,0xe7,0x32,0xdc,0x86,0x55,0xe8,0xc2,
|
||||
0xae,0x9d,0xad,0xb5,0x31,0x23,0x9b,0x8f,0xb2,0x9c,0x65,0xb4,
|
||||
0xa1,0x8b,0xf1,0x7c,0xd6,0x6a,0x52,0x93,0x3f,0x91,0x17,0xc4,
|
||||
0x27,0x61,0x16,0xb6,0xc1,0xba,0xca,0xf1,0x8a,0xdd,0xd2,0xd5,
|
||||
0xb8,0x53,0xa9,0x49,0x1c,0x09,0x44,0x28,0x66,0xd2,0x33,0x81,
|
||||
0x96,0x8a,0x88,0x9c,0x79,0x1a,0xc6,0x61,0x76,0xd4,0x77,0xd6,
|
||||
0x49,0x2f,0x3c,0x0b,0xa9,0x27,0x7f,0x20,0xcf,0xc8,0xef,0x49,
|
||||
0x4a,0x58,0x9d,0x11,0x66,0x69,0x4d,0x43,0xa2,0xd2,0xa0,0x5f,
|
||||
0x13,0x6b,0x73,0x92,0x17,0x8c,0x30,0x3a,0xae,0x53,0xb0,0x41,
|
||||
0xc0,0x19,0xc9,0xb3,0x2b,0x34,0x34,0x24,0x05,0xff,0xc4,0x58,
|
||||
0x4d,0x8a,0xe2,0x25,0x61,0x21,0x23,0x49,0x58,0x4c,0xac,0x58,
|
||||
0xcf,0x43,0xaa,0xb6,0x41,0x93,0x7c,0x64,0xc9,0xf3,0x22,0x23,
|
||||
0x69,0x33,0x51,0x49,0xbb,0x36,0x64,0x64,0x4b,0x72,0x55,0x70,
|
||||
0xc2,0xd8,0x44,0xa5,0x73,0x34,0x68,0x3b,0x22,0x23,0x8c,0xc8,
|
||||
0x68,0xa8,0xe9,0x4d,0x50,0x89,0x12,0x86,0x18,0x6a,0xa8,0x4e,
|
||||
0x03,0x09,0x86,0x7b,0xda,0x6a,0x5b,0x72,0x36,0x5e,0x9b,0xb2,
|
||||
0x60,0x93,0x7a,0x6b,0x78,0x9e,0x74,0x44,0xe8,0xd4,0xe2,0x30,
|
||||
0xac,0x4e,0x6d,0x1e,0x26,0x1f,0xb6,0xfb,0x95,0xd8,0x6e,0x3e,
|
||||
0x14,0xfb,0xb9,0xb7,0x72,0x4a,0x77,0xba,0x2c,0xf9,0x26,0xf8,
|
||||
0x10,0x96,0xce,0x06,0x06,0x91,0xfe,0xfa,0xab,0xa6,0x8c,0x8b,
|
||||
0xaa,0x2c,0xed,0x97,0x9e,0xb2,0x2c,0x85,0xe0,0x54,0x97,0x82,
|
||||
0xcf,0x35,0x36,0xc3,0x9e,0x33,0x63,0xe3,0x77,0x02,0x46,0xef,
|
||||
0x89,0x81,0x62,0xcd,0x72,0x31,0x57,0xac,0x7e,0x99,0xa8,0x1f,
|
||||
0xff,0xf8,0x37,0x6c,0xd0,0x5a,0x56,0x7b,0x36,0x3a,0xb6,0x3f,
|
||||
0x3e,0xfb,0x73,0x5a,0x53,0xf2,0x9c,0x5c,0x91,0x97,0xe4,0x75,
|
||||
0xaf,0x50,0x7f,0x5f,0x25,0x3a,0x51,0x68,0xf8,0xe9,0xd7,0x3d,
|
||||
0xc1,0xc7,0xe1,0x57,0x93,0x51,0xaa,0xae,0x88,0x66,0xf8,0xd6,
|
||||
0x79,0xa2,0x9f,0x93,0xd7,0x14,0x5f,0x95,0x41,0x08,0xe4,0x65,
|
||||
0x8a,0xef,0xd0,0x75,0x95,0xe0,0x7b,0xee,0xa2,0x86,0x41,0x6b,
|
||||
0x4d,0x58,0x84,0xdd,0xfa,0x68,0xde,0x5a,0xe1,0x38,0xea,0xbd,
|
||||
0x56,0x83,0x0e,0x46,0x7e,0xa1,0x61,0x45,0x97,0x8b,0x0d,0x0c,
|
||||
0xc5,0x48,0xc3,0x4d,0x3a,0xdb,0xde,0x45,0xe6,0x2a,0x4e,0x67,
|
||||
0xa6,0xfc,0xca,0xcb,0x70,0x27,0xbc,0xf4,0x52,0x3a,0x61,0xf9,
|
||||
0xa9,0x08,0x22,0x48,0x01,0xf9,0x3b,0xe2,0x12,0x9b,0x9a,0x14,
|
||||
0xea,0xa2,0x8a,0x29,0x5e,0xf3,0x5a,0xa0,0x39,0x76,0x40,0xfa,
|
||||
0x7d,0x61,0x46,0x58,0x09,0x3b,0x73,0x95,0x15,0xf6,0xb4,0x02,
|
||||
0x89,0xf3,0xab,0xd4,0xa5,0x16,0xc5,0xc1,0xfa,0xaa,0xbe,0xe5,
|
||||
0x7e,0x75,0xb0,0x5b,0x05,0x8f,0x0d,0x1d,0x37,0xc4,0x13,0x51,
|
||||
0x33,0x4d,0x6d,0xe2,0x49,0xa8,0x85,0xce,0x2c,0xfd,0x90,0x74,
|
||||
0xf5,0x4e,0xe7,0x36,0x2b,0x68,0x33,0x6e,0xb5,0xb1,0x79,0xd4,
|
||||
0x0d,0x28,0x1b,0xce,0x0f,0xb8,0x83,0x6d,0x68,0x62,0xef,0x89,
|
||||
0x33,0x3f,0xc1,0x5b,0xf2,0x50,0x0e,0x26,0x58,0xe8,0xe1,0xc4,
|
||||
0x1d,0xe1,0xe7,0x72,0xe6,0xc4,0x15,0xd2,0xf7,0x07,0x37,0x83,
|
||||
0xcc,0x27,0x61,0xd1,0xee,0xf4,0x01,0x32,0x2f,0x7a,0x67,0x22,
|
||||
0xbc,0x66,0xff,0xa4,0xa1,0x0d,0x87,0x68,0x6d,0x15,0x8f,0xce,
|
||||
0xf7,0x48,0xe6,0xd3,0x5e,0xe6,0xeb,0x41,0xe6,0x67,0xe6,0x5c,
|
||||
0xaa,0x59,0x5d,0x24,0x96,0x2a,0x4e,0xca,0xc4,0x30,0x55,0x10,
|
||||
0x9b,0x6a,0x5e,0x83,0xa6,0x4a,0x44,0xfa,0xa1,0x3b,0x35,0xac,
|
||||
0x66,0x26,0xb2,0x22,0x7d,0x55,0xb9,0xe2,0x2c,0x43,0x27,0xca,
|
||||
0xa2,0xac,0xb0,0x87,0x1d,0xfa,0xca,0xe2,0x42,0x3f,0xa7,0x11,
|
||||
0xe0,0xdd,0x0b,0xc7,0xa0,0x9d,0xd3,0x98,0xca,0xdd,0xeb,0xe0,
|
||||
0x61,0x14,0x54,0x1b,0x64,0x10,0x2b,0x36,0xbd,0x38,0xbd,0xb8,
|
||||
0x3c,0x7b,0xb4,0x55,0x12,0x9f,0x34,0x46,0xa7,0xfb,0x1a,0x21,
|
||||
0xd1,0xbc,0xaf,0xfe,0xfa,0xee,0x35,0x79,0xf1,0x8c,0x8e,0xb7,
|
||||
0x90,0xc0,0x42,0x1b,0x9e,0x3a,0xd1,0x7a,0xae,0x2c,0x25,0x2f,
|
||||
0x86,0xf3,0x9a,0x61,0x70,0xef,0x0a,0xf5,0x33,0x82,0xf3,0x06,
|
||||
0x0a,0xcd,0x4a,0x62,0x04,0xdb,0xf9,0xd0,0x68,0xfb,0x1c,0x91,
|
||||
0x8c,0x69,0xb9,0x0d,0x7b,0x44,0xbb,0x00,0x41,0xe3,0x58,0x42,
|
||||
0x95,0xc4,0x26,0x91,0x77,0xd8,0x94,0x83,0x18,0x20,0x99,0x92,
|
||||
0x98,0x44,0xa7,0x8a,0xd6,0xc3,0x8a,0xbd,0xf1,0x51,0xcd,0x14,
|
||||
0x41,0x68,0x43,0xfc,0x51,0x84,0x07,0x8b,0x1f,0x4c,0x06,0xb6,
|
||||
0x28,0xa0,0x77,0x4e,0x08,0xcc,0x8d,0xe4,0x3c,0xd9,0x34,0x2c,
|
||||
0x39,0x2e,0x9a,0x71,0x68,0x17,0x37,0xc1,0xec,0x67,0xd6,0x5c,
|
||||
0xcf,0xec,0xe8,0x95,0xb6,0x79,0x66,0x6c,0xc1,0x46,0xb0,0x74,
|
||||
0x57,0xb0,0xb1,0xcf,0xe8,0xcc,0xb3,0x54,0x75,0xf4,0x46,0xef,
|
||||
0xc7,0xca,0x5c,0xc3,0xf5,0xdf,0x18,0xeb,0x72,0x2b,0x7d,0x9e,
|
||||
0xf3,0xce,0x65,0x74,0xef,0xd8,0x8d,0xb1,0xa9,0x22,0x24,0x89,
|
||||
0x27,0x2b,0xa3,0x1c,0xf0,0x63,0x08,0xc7,0x69,0x98,0x2a,0x9c,
|
||||
0x53,0x83,0x07,0x83,0x58,0xa8,0x48,0x6a,0x88,0x44,0x0f,0x42,
|
||||
0xb0,0x84,0x16,0x13,0xc4,0x61,0xcd,0x5c,0xb2,0xaf,0x0b,0x43,
|
||||
0x57,0xe4,0xa8,0x8b,0x92,0x2e,0x6b,0x6b,0x38,0x4f,0x5b,0xed,
|
||||
0x86,0x28,0x27,0x11,0xaf,0xda,0xe0,0x64,0x68,0x6e,0xb6,0x46,
|
||||
0x14,0x69,0xa7,0xde,0x5a,0x76,0x9b,0xec,0x95,0x08,0x5a,0xa0,
|
||||
0x6f,0x17,0x9c,0x93,0xb7,0x68,0x3e,0x9a,0xc2,0x21,0x74,0x0f,
|
||||
0x04,0xbf,0x0d,0xcb,0x30,0x87,0x2d,0x07,0xc1,0x3a,0x15,0x8c,
|
||||
0x70,0x08,0xe0,0x41,0xc1,0xe8,0x06,0x02,0xf6,0x17,0x42,0x1c,
|
||||
0xd3,0x06,0xb8,0x52,0x0c,0x0c,0xdb,0xe0,0x2a,0x3e,0xa5,0xa7,
|
||||
0x75,0x6e,0xd3,0xd0,0xcf,0xc2,0x49,0x18,0x72,0x07,0x79,0x20,
|
||||
0xb0,0x45,0xec,0xea,0xc7,0x74,0xea,0x60,0xab,0xc8,0x20,0x2c,
|
||||
0x5c,0x98,0x4b,0x7e,0xe8,0xb0,0xe9,0x69,0xf0,0x37,0x89,0xd9,
|
||||
0xd1,0xc0,0x58,0xc4,0xc0,0xcf,0xc9,0x2b,0x4e,0x2a,0xbe,0xff,
|
||||
0xa0,0x53,0x38,0x2c,0xbf,0xbc,0x6c,0xec,0x3f,0xe1,0xd1,0xa3,
|
||||
0xaa,0xd4,0xf0,0xda,0x51,0x2d,0x12,0xcb,0x50,0x09,0x2b,0x0f,
|
||||
0x33,0xba,0x1d,0x2a,0x8f,0x7a,0x50,0x39,0x9f,0xdd,0xe3,0xec,
|
||||
0xe9,0x23,0x9e,0x2f,0x8e,0x2c,0x03,0x47,0x68,0x85,0x80,0x2e,
|
||||
0x64,0x88,0x31,0x77,0x16,0x8e,0xb2,0xf4,0xdb,0xf0,0x5e,0x2c,
|
||||
0x3f,0x11,0xef,0x67,0xc4,0x03,0x77,0x5f,0x1e,0x13,0x89,0xf0,
|
||||
0xc4,0xf3,0x8d,0x78,0x3c,0x33,0x4e,0x60,0xde,0xd1,0x21,0xf8,
|
||||
0xb2,0xd5,0xcd,0xce,0x54,0x15,0x5b,0xce,0xb6,0xb6,0x12,0x6c,
|
||||
0xd2,0x1a,0x58,0xd1,0x74,0x8c,0xb6,0xd8,0x05,0x05,0x61,0xa3,
|
||||
0x3a,0x58,0x1e,0xd2,0xa3,0xc6,0xb0,0xc5,0xcc,0x62,0xee,0x64,
|
||||
0x1d,0x57,0xc0,0xd8,0x38,0x11,0x1a,0xe5,0xb7,0xe9,0x1e,0x23,
|
||||
0x3f,0x11,0xff,0x37,0x3f,0x27,0x6e,0x2b,0x72,0x67,0x31,0x9f,
|
||||
0xad,0x94,0x94,0xc9,0x9d,0xa3,0x7b,0xc3,0x57,0x2a,0xc8,0xf1,
|
||||
0x9d,0x67,0x7b,0x2b,0x56,0xba,0xbb,0x9e,0xfc,0xa3,0xcb,0xf6,
|
||||
0xef,0xaf,0x3f,0x6e,0xbb,0xeb,0xc5,0xab,0x4d,0xbe,0x7f,0xb7,
|
||||
0xfb,0x78,0xbd,0x3c,0x74,0x62,0xf3,0x73,0x68,0x5a,0x2f,0x1a,
|
||||
0xeb,0x45,0x6b,0xc3,0x64,0xee,0x39,0xb3,0xa5,0x58,0x9b,0x30,
|
||||
0x19,0xfb,0xe7,0xcc,0x32,0x47,0x7d,0x1a,0x6e,0x7a,0xdb,0xb5,
|
||||
0xc2,0xf3,0x10,0xa3,0x6f,0x1b,0xd5,0x12,0x3e,0xca,0x4e,0xec,
|
||||
0x57,0x87,0x0e,0x81,0x5b,0x40,0x57,0x0d,0x2c,0x7f,0x7d,0x8e,
|
||||
0xb8,0x51,0x55,0x35,0x64,0x0b,0x03,0x65,0x17,0xda,0xbe,0xfd,
|
||||
0x6d,0x86,0xf9,0x40,0xdc,0x45,0x4b,0x6f,0x4e,0x96,0x6e,0x04,
|
||||
0xc8,0x1d,0xd2,0x84,0xb8,0x4d,0x3f,0xb5,0xf4,0x5b,0xd6,0x85,
|
||||
0x63,0xcc,0x21,0xa0,0x1f,0xbb,0xce,0x67,0xc4,0xff,0x8b,0x9f,
|
||||
0x98,0xd4,0x54,0x12,0x73,0x1b,0x25,0x44,0x45,0x3f,0xbc,0xe7,
|
||||
0x2e,0x3c,0x38,0xff,0x30,0xf5,0xc4,0x1d,0x34,0xce,0x16,0xed,
|
||||
0xd6,0xba,0x42,0xb0,0xe5,0x7c,0x67,0x9c,0xe3,0x82,0x76,0xf0,
|
||||
0xc3,0xea,0xec,0xfc,0x9c,0xe9,0x21,0x53,0xfb,0xca,0x9e,0xb2,
|
||||
0x0c,0x2c,0x0c,0xe9,0x57,0x22,0x76,0x09,0x8d,0x0c,0x88,0x5c,
|
||||
0x9d,0x0c,0x0d,0x62,0x74,0x9f,0x85,0xbc,0x88,0x18,0x32,0x66,
|
||||
0x99,0x7b,0x03,0xd6,0x08,0xdb,0x69,0x04,0x04,0x35,0x07,0x8b,
|
||||
0x7d,0x14,0xff,0x56,0xf5,0xb7,0xcb,0xe1,0x53,0x62,0xff,0x48,
|
||||
0x32,0x96,0x02,0x0c,0xd7,0x6f,0x0d,0x2f,0xd9,0x2a,0xdd,0xa8,
|
||||
0x83,0x29,0x0a,0xd6,0x4c,0xda,0x01,0xbf,0xd1,0xc9,0x6c,0xab,
|
||||
0x4b,0xcb,0x0b,0x04,0xdd,0x9d,0x2e,0x0c,0x7b,0x1f,0x31,0xa6,
|
||||
0x8a,0x27,0x03,0xb8,0x90,0x35,0x8f,0xa0,0x72,0x50,0x96,0xcd,
|
||||
0xa8,0xa7,0x37,0xfb,0x1b,0xfd,0x46,0xdb,0xa2,0x14,0x6c,0xca,
|
||||
0xe6,0x9b,0xb9,0x39,0x18,0x17,0x2b,0x0d,0x9b,0xcf,0x17,0xd6,
|
||||
0x1c,0x90,0x73,0xe5,0x94,0x4d,0x37,0xf3,0xcd,0xf7,0xe6,0xf0,
|
||||
0x4b,0x99,0xed,0x3e,0xd2,0xf7,0xfb,0x9b,0xee,0x95,0x96,0x4b,
|
||||
0xcb,0xd7,0x9e,0x5a,0x79,0xe3,0xb9,0x16,0x13,0xc3,0xd6,0x80,
|
||||
0x3a,0x12,0xee,0xaa,0x44,0xf4,0x1d,0x38,0x10,0x52,0x19,0xce,
|
||||
0x9f,0xea,0x12,0xe6,0x64,0x2d,0xe2,0xff,0x1c,0x49,0x88,0xd3,
|
||||
0x8d,0x2a,0x2d,0x9b,0x42,0x77,0xdc,0xa6,0x31,0x89,0xd1,0x1e,
|
||||
0xe4,0x2a,0xb0,0x25,0x09,0x20,0x1b,0x98,0x54,0xdc,0xa5,0x5d,
|
||||
0xfd,0xd6,0xf6,0x29,0xc3,0x02,0xbc,0xcd,0xb5,0xeb,0xa7,0x68,
|
||||
0x04,0x95,0xae,0x3e,0x18,0xa8,0x75,0x40,0xcc,0x73,0xdd,0x2f,
|
||||
0x8c,0x83,0x5a,0xe4,0x9a,0xa0,0x99,0xed,0xd3,0x4f,0xbf,0x16,
|
||||
0x8b,0xc8,0x88,0x13,0xe0,0xa2,0x12,0xf0,0x28,0x89,0x06,0x45,
|
||||
0xd5,0x65,0x93,0xf4,0xe1,0x0e,0x76,0x03,0x38,0x12,0xb3,0x6f,
|
||||
0x89,0x3c,0xab,0x23,0x59,0x0c,0x24,0xeb,0x49,0x77,0x22,0x49,
|
||||
0x24,0xa1,0x43,0x76,0x11,0xc3,0x1e,0x63,0xe6,0x2f,0x11,0xc1,
|
||||
0x9b,0x14,0xf7,0x0d,0xbc,0xae,0x00,0x77,0xcf,0x2f,0x7d,0x7a,
|
||||
0xe5,0xe7,0x17,0x01,0x2e,0x3e,0xbd,0x5e,0x26,0xfd,0x36,0xda,
|
||||
0x8c,0xf2,0x0c,0x37,0x92,0xd9,0x57,0x09,0x19,0x3c,0xb0,0x82,
|
||||
0x06,0x40,0x30,0x11,0x41,0x65,0xa3,0x6c,0x54,0x94,0x79,0x0e,
|
||||
0xdc,0x54,0xd8,0x73,0xe4,0x05,0xec,0xcf,0x8b,0xec,0x39,0x83,
|
||||
0xf1,0x6b,0x44,0x97,0xcd,0x72,0xc5,0x3e,0xf0,0x12,0xe7,0x4c,
|
||||
0x23,0xee,0x69,0xf7,0xcb,0xe6,0x1d,0xaa,0xf6,0xad,0x39,0xb6,
|
||||
0x11,0x9b,0x2c,0x37,0x4d,0x53,0x7f,0x66,0xc0,0xdb,0xb6,0xdb,
|
||||
0xa0,0x73,0x75,0x02,0x2e,0x46,0x6a,0x58,0xe3,0x38,0xd0,0x87,
|
||||
0xea,0x3c,0x74,0xab,0xf0,0xc1,0x97,0x30,0x01,0xa4,0x96,0x5b,
|
||||
0x6f,0x11,0xb0,0x26,0x9e,0x5b,0x19,0x91,0x5e,0x0f,0xf8,0xfa,
|
||||
0x1a,0x1a,0x43,0x21,0x6d,0x98,0x79,0x12,0x41,0xf4,0x19,0xde,
|
||||
0xf4,0xa8,0x3a,0x8f,0x0a,0xc1,0xd3,0x78,0x78,0x9d,0x95,0x06,
|
||||
0x2b,0xcc,0x31,0x9c,0x5d,0x56,0x97,0x01,0xf7,0xc5,0xe9,0xed,
|
||||
0x79,0x93,0xe3,0x69,0x93,0xe1,0xa4,0x3d,0xaa,0x3c,0x6d,0xe2,
|
||||
0x87,0x4d,0xe8,0xda,0xc9,0x29,0xb2,0x05,0x00,0x4d,0x3f,0x06,
|
||||
0x17,0x27,0x90,0x05,0x55,0x36,0xda,0xe6,0x25,0x09,0xc4,0x75,
|
||||
0x63,0xff,0x12,0xde,0x07,0x5f,0x54,0x9f,0xeb,0xb2,0x77,0x48,
|
||||
0x84,0x5c,0x8d,0xbb,0x49,0x0d,0x07,0x94,0x08,0xd7,0xb3,0x60,
|
||||
0xb4,0x7b,0xcd,0xde,0x03,0xac,0xc7,0x00,0x84,0x38,0x3d,0x0e,
|
||||
0x4f,0x88,0x6e,0xb7,0xe9,0x31,0x5f,0x3c,0xd5,0xd3,0xa2,0xbb,
|
||||
0xaf,0x0e,0xb8,0xf3,0xb3,0x74,0xa9,0x78,0x13,0x03,0x1b,0x10,
|
||||
0xd8,0x40,0xb8,0x4a,0x3c,0x41,0x40,0x30,0x9d,0xbe,0x44,0xfe,
|
||||
0xa7,0x12,0x91,0x6e,0x85,0xe2,0xac,0x3d,0xd8,0xa3,0x39,0x02,
|
||||
0xd5,0x44,0xf4,0x1e,0x8c,0x3b,0xdd,0x0a,0x51,0x62,0x3c,0xec,
|
||||
0x23,0x62,0xf8,0xc2,0x83,0x61,0x47,0xe7,0x4f,0x30,0xa6,0xe2,
|
||||
0xf0,0xe4,0xff,0xe5,0xef,0xb0,0x3b,0x02,0xb8,0x40,0x73,0xec,
|
||||
0x2b,0x1a,0xee,0xab,0xf5,0xa7,0x56,0xf4,0xd9,0x27,0x88,0x41,
|
||||
0xc1,0x76,0xbb,0xdf,0x45,0x50,0x3d,0x79,0x52,0xa2,0xb1,0xba,
|
||||
0x0e,0x7b,0x58,0xae,0x2c,0xca,0x47,0x81,0x99,0x64,0x95,0xec,
|
||||
0xaf,0x87,0x5f,0xe3,0x66,0x13,0xa2,0x47,0xac,0xfa,0x2b,0xdf,
|
||||
0xbd,0xad,0x5a,0x29,0x3a,0xcc,0xf4,0xf2,0x09,0x23,0x02,0x89,
|
||||
0x68,0x86,0x45,0xb8,0x2f,0xaf,0xd7,0x61,0xcc,0x49,0x95,0xb8,
|
||||
0xd4,0x51,0x0b,0x07,0x8d,0x46,0xfe,0x9d,0x9c,0x85,0xc9,0xea,
|
||||
0xe4,0x00,0xe7,0x59,0xfd,0x15,0xda,0x3d,0xb1,0x96,0xa0,0x2e,
|
||||
0x34,0xc6,0x3f,0x71,0xfa,0x4f,0x64,0x21,0xc3,0xdd,0xdd,0x7e,
|
||||
0xa3,0x5c,0xef,0xc6,0xeb,0xfd,0xbc,0x9b,0x34,0x53,0x5e,0xe0,
|
||||
0xc4,0xf6,0xa0,0x77,0xb3,0x4d,0x2f,0x0c,0xb0,0x80,0x50,0xad,
|
||||
0x64,0xed,0xe3,0x51,0x88,0x19,0xc9,0xf9,0xdc,0x17,0x7c,0x6b,
|
||||
0xf6,0x4d,0xf3,0xbe,0x7c,0xd3,0xee,0x96,0xfc,0xdd,0x1b,0xb7,
|
||||
0xeb,0x36,0xd3,0xbf,0xe5,0xd7,0x7f,0x6f,0xff,0xe3,0xb3,0x8d,
|
||||
0x09,0x62,0xb2,0x75,0xa2,0xd1,0x4e,0xd2,0x16,0x57,0x99,0x1b,
|
||||
0x5b,0xb2,0xb1,0x2e,0x71,0x67,0xe3,0x65,0x56,0x14,0x45,0x2e,
|
||||
0x72,0x5e,0x72,0x1b,0x97,0x8e,0xff,0xcf,0x78,0x08,0x44,0x08,
|
||||
0x45,0xc8,0x23,0x79,0x35,0x04,0x22,0x8b,0x41,0x28,0xa0,0xf3,
|
||||
0x22,0xce,0x2a,0xf2,0xac,0xcc,0x62,0xd3,0x88,0x8d,0xd8,0x79,
|
||||
0x76,0x5f,0x52,0x3d,0x14,0xa1,0x50,0x10,0x36,0xe4,0x31,0x62,
|
||||
0x5d,0xc3,0x81,0x6e,0x81,0x72,0x55,0x91,0x3e,0x26,0xa1,0x1c,
|
||||
0x8c,0x50,0x74,0xb1,0x75,0xb8,0x15,0x8c,0x91,0xc8,0x1e,0x5e,
|
||||
0xbc,0x41,0xde,0x10,0x8f,0xdb,0xf0,0x72,0x7c,0x61,0x5c,0xf2,
|
||||
0x94,0x85,0x0b,0xd6,0xe2,0x37,0x07,0x72,0x3c,0xbf,0x00,0x45,
|
||||
0x31,0x9e,0x2d,0xb6,0x26,0x26,0x9b,0x8b,0x0e,0xe0,0xb9,0x66,
|
||||
0x4e,0xf8,0xef,0xc4,0x2f,0x21,0x48,0xff,0xc2,0x91,0xef,0xfe,
|
||||
0x0b,0x06,0x62,0xc2,0xe3,0x11,0x13,0x00,0x00,
|
||||
};
|
||||
|
||||
const GFXglyph Oswald_Medium20pt7bGlyphs[] PROGMEM = {
|
||||
{ 0, 1, 1, 10, 0, 0 }, // 0x20 ' '
|
||||
{ 1, 5, 32, 9, 2, -31 }, // 0x21 '!'
|
||||
{ 21, 11, 11, 12, 1, -31 }, // 0x22 '"'
|
||||
{ 37, 17, 32, 20, 2, -31 }, // 0x23 '#'
|
||||
{ 105, 17, 39, 19, 1, -34 }, // 0x24 '$'
|
||||
{ 188, 34, 32, 37, 2, -31 }, // 0x25 '%'
|
||||
{ 324, 19, 32, 23, 2, -31 }, // 0x26 '&'
|
||||
{ 400, 4, 11, 6, 1, -31 }, // 0x27 '''
|
||||
{ 406, 8, 39, 13, 3, -31 }, // 0x28 '('
|
||||
{ 445, 9, 39, 12, 1, -31 }, // 0x29 ')'
|
||||
{ 489, 13, 14, 16, 2, -31 }, // 0x2A '*'
|
||||
{ 512, 15, 16, 17, 1, -23 }, // 0x2B '+'
|
||||
{ 542, 5, 10, 8, 2, -4 }, // 0x2C ','
|
||||
{ 549, 10, 3, 12, 1, -12 }, // 0x2D '-'
|
||||
{ 553, 5, 5, 9, 2, -4 }, // 0x2E '.'
|
||||
{ 557, 13, 32, 16, 1, -31 }, // 0x2F '/'
|
||||
{ 609, 17, 32, 21, 2, -31 }, // 0x30 '0'
|
||||
{ 677, 10, 32, 15, 1, -31 }, // 0x31 '1'
|
||||
{ 717, 16, 32, 19, 2, -31 }, // 0x32 '2'
|
||||
{ 781, 16, 32, 19, 2, -31 }, // 0x33 '3'
|
||||
{ 845, 18, 32, 20, 1, -31 }, // 0x34 '4'
|
||||
{ 917, 16, 32, 19, 2, -31 }, // 0x35 '5'
|
||||
{ 981, 17, 32, 20, 2, -31 }, // 0x36 '6'
|
||||
{ 1049, 14, 32, 16, 1, -31 }, // 0x37 '7'
|
||||
{ 1105, 16, 32, 20, 2, -31 }, // 0x38 '8'
|
||||
{ 1169, 16, 32, 20, 2, -31 }, // 0x39 '9'
|
||||
{ 1233, 6, 18, 9, 2, -20 }, // 0x3A ':'
|
||||
{ 1247, 6, 25, 10, 2, -21 }, // 0x3B ';'
|
||||
{ 1266, 11, 17, 15, 2, -24 }, // 0x3C '<'
|
||||
{ 1290, 13, 11, 17, 2, -21 }, // 0x3D '='
|
||||
{ 1308, 12, 17, 15, 2, -24 }, // 0x3E '>'
|
||||
{ 1334, 15, 32, 19, 2, -31 }, // 0x3F '?'
|
||||
{ 1394, 33, 37, 36, 2, -31 }, // 0x40 '@'
|
||||
{ 1547, 19, 32, 20, 1, -31 }, // 0x41 'A'
|
||||
{ 1623, 18, 32, 22, 2, -31 }, // 0x42 'B'
|
||||
{ 1695, 18, 32, 21, 2, -31 }, // 0x43 'C'
|
||||
{ 1767, 18, 32, 22, 2, -31 }, // 0x44 'D'
|
||||
{ 1839, 14, 32, 17, 2, -31 }, // 0x45 'E'
|
||||
{ 1895, 13, 32, 16, 2, -31 }, // 0x46 'F'
|
||||
{ 1947, 18, 32, 22, 2, -31 }, // 0x47 'G'
|
||||
{ 2019, 18, 32, 23, 2, -31 }, // 0x48 'H'
|
||||
{ 2091, 5, 32, 11, 3, -31 }, // 0x49 'I'
|
||||
{ 2111, 10, 33, 13, 0, -31 }, // 0x4A 'J'
|
||||
{ 2153, 19, 32, 21, 2, -31 }, // 0x4B 'K'
|
||||
{ 2229, 14, 32, 16, 2, -31 }, // 0x4C 'L'
|
||||
{ 2285, 22, 32, 27, 2, -31 }, // 0x4D 'M'
|
||||
{ 2373, 17, 32, 21, 2, -31 }, // 0x4E 'N'
|
||||
{ 2441, 18, 32, 22, 2, -31 }, // 0x4F 'O'
|
||||
{ 2513, 18, 32, 21, 2, -31 }, // 0x50 'P'
|
||||
{ 2585, 18, 38, 22, 2, -31 }, // 0x51 'Q'
|
||||
{ 2671, 18, 32, 22, 2, -31 }, // 0x52 'R'
|
||||
{ 2743, 16, 32, 19, 2, -31 }, // 0x53 'S'
|
||||
{ 2807, 15, 32, 17, 1, -31 }, // 0x54 'T'
|
||||
{ 2867, 18, 32, 22, 2, -31 }, // 0x55 'U'
|
||||
{ 2939, 18, 32, 20, 1, -31 }, // 0x56 'V'
|
||||
{ 3011, 26, 32, 28, 1, -31 }, // 0x57 'W'
|
||||
{ 3115, 19, 32, 20, 0, -31 }, // 0x58 'X'
|
||||
{ 3191, 19, 32, 19, 0, -31 }, // 0x59 'Y'
|
||||
{ 3267, 15, 32, 17, 1, -31 }, // 0x5A 'Z'
|
||||
{ 3327, 9, 39, 13, 2, -31 }, // 0x5B '['
|
||||
{ 3371, 13, 32, 16, 1, -31 }, // 0x5C '\'
|
||||
{ 3423, 9, 39, 12, 1, -31 }, // 0x5D ']'
|
||||
{ 3467, 16, 13, 18, 1, -31 }, // 0x5E '^'
|
||||
{ 3493, 14, 4, 14, 0, 3 }, // 0x5F '_'
|
||||
{ 3500, 8, 8, 11, 2, -31 }, // 0x60 '`'
|
||||
{ 3508, 15, 23, 17, 1, -22 }, // 0x61 'a'
|
||||
{ 3552, 15, 32, 19, 2, -31 }, // 0x62 'b'
|
||||
{ 3612, 14, 23, 17, 2, -22 }, // 0x63 'c'
|
||||
{ 3653, 15, 32, 19, 2, -31 }, // 0x64 'd'
|
||||
{ 3713, 14, 23, 17, 2, -22 }, // 0x65 'e'
|
||||
{ 3754, 11, 31, 12, 1, -30 }, // 0x66 'f'
|
||||
{ 3797, 18, 31, 18, 1, -23 }, // 0x67 'g'
|
||||
{ 3867, 15, 32, 19, 2, -31 }, // 0x68 'h'
|
||||
{ 3927, 6, 31, 10, 2, -30 }, // 0x69 'i'
|
||||
{ 3951, 9, 37, 10, -1, -30 }, // 0x6A 'j'
|
||||
{ 3993, 16, 32, 18, 2, -31 }, // 0x6B 'k'
|
||||
{ 4057, 6, 32, 10, 2, -31 }, // 0x6C 'l'
|
||||
{ 4081, 24, 23, 28, 2, -22 }, // 0x6D 'm'
|
||||
{ 4150, 15, 23, 19, 2, -22 }, // 0x6E 'n'
|
||||
{ 4194, 14, 23, 18, 2, -22 }, // 0x6F 'o'
|
||||
{ 4235, 15, 30, 19, 2, -22 }, // 0x70 'p'
|
||||
{ 4292, 15, 30, 19, 2, -22 }, // 0x71 'q'
|
||||
{ 4349, 11, 23, 14, 2, -22 }, // 0x72 'r'
|
||||
{ 4381, 14, 23, 16, 1, -22 }, // 0x73 's'
|
||||
{ 4422, 11, 29, 13, 1, -28 }, // 0x74 't'
|
||||
{ 4462, 14, 23, 19, 2, -22 }, // 0x75 'u'
|
||||
{ 4503, 15, 23, 16, 0, -22 }, // 0x76 'v'
|
||||
{ 4547, 21, 23, 23, 1, -22 }, // 0x77 'w'
|
||||
{ 4608, 16, 23, 16, 0, -22 }, // 0x78 'x'
|
||||
{ 4654, 16, 29, 16, 0, -22 }, // 0x79 'y'
|
||||
{ 4712, 13, 23, 15, 1, -22 }, // 0x7A 'z'
|
||||
{ 4750, 10, 40, 13, 2, -31 }, // 0x7B '{'
|
||||
{ 4800, 4, 38, 10, 3, -31 }, // 0x7C '|'
|
||||
{ 4819, 10, 40, 14, 2, -31 }, // 0x7D '}'
|
||||
{ 4869, 16, 6, 18, 1, -18 } }; // 0x7E '~'
|
||||
|
||||
// const GFXfont Oswald_Medium20pt7b PROGMEM = {
|
||||
// (uint8_t *)Oswald_Medium20pt7bBitmaps,
|
||||
// (GFXglyph *)Oswald_Medium20pt7bGlyphs,
|
||||
// 0x20, 0x7E, 58 };
|
||||
|
||||
// // Approx. 5553 bytes
|
||||
|
||||
|
||||
// Font properties
|
||||
static constexpr FontData Oswald_Medium20pt7b_Properties = {
|
||||
Oswald_Medium20pt7bBitmaps_Gzip,
|
||||
Oswald_Medium20pt7bGlyphs,
|
||||
sizeof(Oswald_Medium20pt7bBitmaps_Gzip),
|
||||
5553, // Original size
|
||||
0x20, // First char
|
||||
0x7E, // Last char
|
||||
58 // yAdvance
|
||||
};
|
|
@ -1,497 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Arduino.h>
|
||||
#include "fonts.hpp"
|
||||
|
||||
const uint8_t Oswald_Medium30pt7bBitmaps_Gzip[] = {
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xcd, 0x5a,
|
||||
0xbb, 0x72, 0x23, 0x3b, 0x92, 0x05, 0xb7, 0x8c, 0x72, 0x26, 0x1a, 0x63,
|
||||
0x5e, 0x63, 0xe2, 0x62, 0x3f, 0x61, 0xcc, 0x36, 0xee, 0x08, 0xf7, 0x53,
|
||||
0xf6, 0x13, 0xc6, 0x6c, 0x43, 0x4b, 0x94, 0x82, 0x86, 0xbc, 0xd1, 0x27,
|
||||
0xf0, 0x53, 0x04, 0x86, 0x0c, 0x99, 0xfc, 0x83, 0x65, 0x31, 0x68, 0xd0,
|
||||
0x14, 0x14, 0x32, 0x08, 0x86, 0x4a, 0xc8, 0x3d, 0x27, 0xc1, 0xa7, 0xd4,
|
||||
0x52, 0xf7, 0xbd, 0xdd, 0x3b, 0xb1, 0x25, 0x50, 0x55, 0x85, 0x42, 0xe1,
|
||||
0x91, 0x48, 0x9c, 0x3c, 0x99, 0x28, 0x23, 0x3c, 0x0a, 0x8f, 0xf1, 0xee,
|
||||
0xb8, 0xac, 0xc7, 0x6f, 0x7a, 0x7c, 0xd6, 0xc3, 0x18, 0x23, 0xbb, 0xa3,
|
||||
0x78, 0x71, 0x72, 0xb3, 0xb8, 0x7f, 0x58, 0x6d, 0x96, 0xcf, 0xe9, 0x25,
|
||||
0x8f, 0x87, 0x8b, 0xb1, 0xbb, 0xb0, 0xd3, 0x76, 0x3e, 0x59, 0xcf, 0x56,
|
||||
0xfd, 0x63, 0xff, 0x25, 0xfd, 0x66, 0x46, 0x83, 0xeb, 0x42, 0xdb, 0xbb,
|
||||
0x49, 0x6e, 0xfb, 0x61, 0x94, 0xbd, 0x19, 0xdb, 0xe8, 0x26, 0x09, 0x37,
|
||||
0x0d, 0x6e, 0x8a, 0x8b, 0xbe, 0x4d, 0x76, 0x96, 0x9b, 0x34, 0x36, 0x28,
|
||||
0xe9, 0xdb, 0x25, 0x2a, 0x0e, 0x22, 0x0b, 0x91, 0x8d, 0xc8, 0x0b, 0x6e,
|
||||
0x76, 0xaf, 0x15, 0x1b, 0xfd, 0x35, 0x4a, 0x0e, 0x28, 0x69, 0xf7, 0x3d,
|
||||
0x90, 0x5b, 0x91, 0x07, 0x91, 0x67, 0xf4, 0xa5, 0xd6, 0x79, 0x6c, 0xc0,
|
||||
0xb2, 0x81, 0x80, 0x3a, 0xd1, 0x40, 0x7f, 0xda, 0x40, 0xef, 0xee, 0x72,
|
||||
0x9b, 0x0a, 0x4b, 0x9a, 0xbf, 0x19, 0xf3, 0xc9, 0x98, 0xd6, 0x98, 0xa6,
|
||||
0x33, 0xa3, 0x68, 0xda, 0xc1, 0x38, 0xe9, 0x9c, 0xf4, 0x4e, 0xb2, 0x43,
|
||||
0x95, 0x22, 0x76, 0x90, 0x79, 0x76, 0xeb, 0xdc, 0x3e, 0x0d, 0xcd, 0x4b,
|
||||
0x19, 0x15, 0x31, 0x12, 0xba, 0x70, 0x1b, 0xff, 0xe6, 0x92, 0xb1, 0x83,
|
||||
0x69, 0x8a, 0x19, 0x49, 0x67, 0xa4, 0x37, 0x7e, 0x30, 0x56, 0x4c, 0x23,
|
||||
0xd1, 0x48, 0x7a, 0x73, 0xd3, 0xd6, 0x62, 0x01, 0xc5, 0xf0, 0x5a, 0xfe,
|
||||
0xd2, 0xc8, 0x30, 0x12, 0x54, 0x25, 0x5d, 0x90, 0x58, 0x9b, 0xb3, 0xcf,
|
||||
0x43, 0xbb, 0x95, 0xeb, 0x21, 0x50, 0xaa, 0xc5, 0x4a, 0x69, 0xa4, 0x18,
|
||||
0x24, 0x37, 0x18, 0x93, 0x8c, 0xf9, 0xfc, 0xaa, 0xaf, 0x68, 0x1a, 0xb9,
|
||||
0xa8, 0xfd, 0xd2, 0x98, 0x20, 0xc6, 0x76, 0xe8, 0x78, 0x42, 0x16, 0xdf,
|
||||
0xba, 0xe4, 0x0c, 0xf5, 0xcc, 0xa2, 0xb0, 0x0c, 0xc4, 0xdc, 0x21, 0x6b,
|
||||
0xdc, 0x64, 0x66, 0xc5, 0x30, 0x62, 0x96, 0xed, 0xc7, 0x78, 0x71, 0x34,
|
||||
0x20, 0xcb, 0xf6, 0xfe, 0x2a, 0xa1, 0xc6, 0x16, 0x02, 0x32, 0xbe, 0x2b,
|
||||
0xc8, 0x6a, 0x93, 0x9b, 0xa1, 0xfa, 0xd2, 0x64, 0x64, 0xd9, 0x18, 0x6c,
|
||||
0xc4, 0x73, 0xcb, 0x2c, 0x3f, 0xc9, 0x17, 0xe8, 0x44, 0x66, 0x16, 0x1a,
|
||||
0x5e, 0x22, 0xcb, 0x4a, 0xbe, 0x68, 0x30, 0x18, 0x4c, 0x10, 0x86, 0x21,
|
||||
0xb3, 0x1c, 0xa4, 0x93, 0xfe, 0x02, 0xd9, 0xa3, 0xd8, 0xde, 0x61, 0x98,
|
||||
0x66, 0x94, 0xc3, 0x0d, 0x0a, 0x5e, 0xd8, 0xbe, 0xe0, 0xdd, 0xbb, 0xec,
|
||||
0x22, 0xb3, 0x9a, 0xac, 0x59, 0x01, 0x59, 0xb3, 0xa1, 0xed, 0x91, 0xe5,
|
||||
0x3b, 0x0c, 0x16, 0xea, 0xe0, 0xd0, 0x83, 0x58, 0x38, 0x40, 0x14, 0x65,
|
||||
0xa7, 0xd0, 0x5f, 0x4c, 0x4e, 0xd4, 0x57, 0xb2, 0xbe, 0xe2, 0xd1, 0x9f,
|
||||
0xde, 0x60, 0x2e, 0x67, 0x78, 0x25, 0x37, 0x03, 0xb2, 0x1c, 0xfa, 0xc3,
|
||||
0xac, 0x56, 0x98, 0x45, 0xa9, 0x20, 0xab, 0xc5, 0x1c, 0xa0, 0x6e, 0xdf,
|
||||
0x1b, 0xde, 0x72, 0x1a, 0x32, 0x24, 0xd5, 0x43, 0xfa, 0x9d, 0x45, 0x39,
|
||||
0xb1, 0xa9, 0x4d, 0xbe, 0xf3, 0x57, 0x03, 0xeb, 0x63, 0xcd, 0x68, 0xa3,
|
||||
0x41, 0xab, 0x2e, 0x16, 0x68, 0x5c, 0x4f, 0x0d, 0x72, 0xf7, 0xc9, 0x14,
|
||||
0x6f, 0x9a, 0xe7, 0x6c, 0xec, 0x02, 0x93, 0x98, 0x75, 0x2e, 0x6d, 0xa6,
|
||||
0xb0, 0x4c, 0xc3, 0x1e, 0x27, 0x37, 0x91, 0x6e, 0xf0, 0xa5, 0x5d, 0x49,
|
||||
0xf2, 0x6e, 0x31, 0xdb, 0x96, 0xb1, 0x9b, 0xaf, 0x72, 0x09, 0x76, 0xbd,
|
||||
0x4e, 0xc1, 0xfb, 0x2b, 0xd9, 0x0e, 0xad, 0xdc, 0xf4, 0x1e, 0xdd, 0x94,
|
||||
0x55, 0x6e, 0xa1, 0xf4, 0xae, 0x2f, 0x23, 0xb9, 0x86, 0x92, 0xb9, 0xa5,
|
||||
0x14, 0xe8, 0xf1, 0x13, 0x7e, 0x37, 0xf2, 0x34, 0x78, 0xb1, 0x3d, 0xf4,
|
||||
0xc1, 0xfc, 0xd3, 0x7c, 0xa6, 0x7a, 0x2f, 0x6e, 0xc7, 0x2f, 0xcf, 0xeb,
|
||||
0xf9, 0xf4, 0xe2, 0x32, 0x99, 0x2f, 0x36, 0x16, 0x9b, 0xe4, 0x66, 0x08,
|
||||
0x6d, 0x46, 0x87, 0x51, 0x03, 0xba, 0xce, 0xb3, 0xe0, 0x7e, 0x86, 0x47,
|
||||
0x61, 0x72, 0xc8, 0xc4, 0x48, 0x38, 0x0a, 0xbd, 0xd9, 0x3f, 0xa9, 0xc5,
|
||||
0xf6, 0x8f, 0xeb, 0xbf, 0x62, 0x07, 0xdf, 0x0b, 0xc5, 0xfb, 0x2b, 0x56,
|
||||
0x0f, 0xcb, 0x66, 0x1f, 0x71, 0xef, 0xf4, 0x7d, 0x3e, 0xd9, 0x55, 0x32,
|
||||
0x9c, 0x55, 0xf2, 0xb5, 0x9b, 0x93, 0x86, 0xfd, 0x8c, 0x67, 0x3c, 0x9c,
|
||||
0x0c, 0xe1, 0xae, 0xf0, 0x51, 0x0f, 0xfd, 0x1d, 0x74, 0x22, 0x0c, 0xd4,
|
||||
0xcf, 0x76, 0xbf, 0x5c, 0x7e, 0x7e, 0x5a, 0xcd, 0x65, 0x1c, 0x38, 0xc8,
|
||||
0x7b, 0x49, 0x58, 0x20, 0x0e, 0x93, 0x42, 0x6d, 0x7f, 0x4e, 0x76, 0x1e,
|
||||
0x87, 0x71, 0xbb, 0xcc, 0x9f, 0xac, 0xf9, 0xfb, 0x7f, 0x72, 0x2a, 0x9b,
|
||||
0x9e, 0x0a, 0x7e, 0x71, 0x7e, 0xd9, 0xc8, 0xe9, 0x91, 0xde, 0x16, 0x38,
|
||||
0x5c, 0xee, 0xcb, 0xb4, 0xad, 0x75, 0x17, 0x97, 0x5f, 0xfe, 0x7e, 0xf2,
|
||||
0xd6, 0xfe, 0x82, 0x65, 0x2f, 0x8d, 0xeb, 0x4e, 0x4f, 0xb6, 0x6b, 0xd2,
|
||||
0xab, 0xd3, 0x18, 0xa7, 0xd1, 0xe9, 0x29, 0xe2, 0x74, 0xf1, 0xea, 0xd4,
|
||||
0xc6, 0x51, 0x3e, 0x3d, 0xa1, 0x27, 0xfe, 0xec, 0xe4, 0x28, 0x08, 0x23,
|
||||
0x1d, 0x74, 0xb1, 0xe1, 0x3a, 0x97, 0x2b, 0x91, 0xa5, 0x76, 0x66, 0x90,
|
||||
0x49, 0x09, 0x51, 0x7c, 0x0c, 0x0b, 0x60, 0x42, 0xc2, 0x12, 0x82, 0x32,
|
||||
0xa3, 0xcc, 0x11, 0x2c, 0xbc, 0xfc, 0xd0, 0x83, 0x97, 0xd2, 0x14, 0x19,
|
||||
0x49, 0x98, 0x89, 0x7f, 0x84, 0xbe, 0xc9, 0xb5, 0xc8, 0x4c, 0x24, 0x2a,
|
||||
0x0a, 0x45, 0xe2, 0x15, 0x30, 0xd1, 0x8c, 0xb1, 0x16, 0x72, 0x78, 0x3a,
|
||||
0x91, 0xee, 0x3d, 0xa6, 0xdf, 0xf5, 0xe8, 0x56, 0x27, 0xa3, 0xd2, 0x0c,
|
||||
0x6d, 0xfe, 0x73, 0xb7, 0x26, 0x44, 0x8c, 0x19, 0x5d, 0x1b, 0x69, 0xb3,
|
||||
0x18, 0x73, 0xc6, 0x98, 0xd1, 0x19, 0x8c, 0xf9, 0x36, 0x7a, 0x8c, 0xf9,
|
||||
0x21, 0xd9, 0x4d, 0x6e, 0x9f, 0x0f, 0x58, 0x4b, 0x31, 0xe1, 0x2d, 0xe3,
|
||||
0x23, 0x97, 0x30, 0x12, 0x60, 0x97, 0xc8, 0x4b, 0x14, 0x65, 0x6a, 0x33,
|
||||
0x31, 0xbb, 0x55, 0x20, 0xae, 0x09, 0x4a, 0x54, 0x13, 0xdf, 0xac, 0x09,
|
||||
0xef, 0xef, 0xaa, 0x50, 0x7b, 0xb1, 0x50, 0x93, 0xb1, 0x51, 0xab, 0x01,
|
||||
0xfb, 0x02, 0x94, 0x8d, 0x87, 0x99, 0x98, 0x68, 0xaf, 0xb6, 0x58, 0x64,
|
||||
0x72, 0x55, 0xd0, 0xe3, 0xdb, 0x2a, 0xbe, 0xde, 0x42, 0x45, 0x59, 0x66,
|
||||
0x18, 0x01, 0xc1, 0x50, 0x77, 0xe8, 0x34, 0xb1, 0xe2, 0x86, 0xaf, 0x03,
|
||||
0xe8, 0xa3, 0xc2, 0x3b, 0x0d, 0x08, 0x40, 0xb8, 0x63, 0x5f, 0xd1, 0x4b,
|
||||
0xf4, 0x6c, 0xff, 0x86, 0x74, 0x5e, 0xb1, 0x5e, 0xab, 0xda, 0x0d, 0x70,
|
||||
0xc4, 0x91, 0xfb, 0xc4, 0x99, 0xb8, 0xdf, 0xcf, 0x04, 0x9a, 0x8b, 0x44,
|
||||
0x2d, 0xa8, 0x30, 0x2d, 0x8c, 0xda, 0x0d, 0x0c, 0x97, 0x23, 0x8b, 0x04,
|
||||
0x1f, 0x35, 0x2c, 0xbd, 0x09, 0x6a, 0xaa, 0x1a, 0x58, 0x80, 0xff, 0xc6,
|
||||
0xda, 0xb9, 0x8d, 0xcd, 0x26, 0x9b, 0x01, 0x0b, 0x68, 0xda, 0xb7, 0xab,
|
||||
0xc1, 0x0c, 0xa1, 0x73, 0x37, 0xa9, 0x5d, 0x16, 0x33, 0x78, 0x98, 0x4d,
|
||||
0x98, 0x5e, 0x81, 0x1d, 0xee, 0xb1, 0x20, 0x5b, 0x5d, 0xfa, 0x0a, 0x0b,
|
||||
0x3d, 0xd6, 0x29, 0x16, 0xa9, 0xbc, 0x3d, 0x12, 0xa5, 0x05, 0xd9, 0x2a,
|
||||
0x68, 0xf6, 0x0a, 0x72, 0x18, 0x2e, 0xc6, 0xc3, 0xc1, 0x9c, 0x3e, 0x0a,
|
||||
0x98, 0xbe, 0xaf, 0x26, 0xbc, 0x00, 0x45, 0x3a, 0x4f, 0xa5, 0x26, 0x1f,
|
||||
0x0b, 0x75, 0x9d, 0x53, 0xaf, 0xa9, 0x40, 0xe9, 0x45, 0x67, 0xbb, 0xe8,
|
||||
0x84, 0xbf, 0x4d, 0xf2, 0x3a, 0x69, 0x79, 0x4c, 0x90, 0xb6, 0xe5, 0x69,
|
||||
0xab, 0xa1, 0xe0, 0x3d, 0xa5, 0x46, 0xdb, 0x57, 0x95, 0x8c, 0x4b, 0x60,
|
||||
0xbf, 0xb0, 0xf6, 0xd3, 0x09, 0x2d, 0x9c, 0xf6, 0x6e, 0x5e, 0x57, 0x43,
|
||||
0xe6, 0xb8, 0x8e, 0xf3, 0x89, 0x61, 0xb9, 0xd5, 0x60, 0x37, 0x72, 0xfd,
|
||||
0x2c, 0x2b, 0xea, 0x00, 0xfb, 0x26, 0xa1, 0x97, 0xdb, 0xde, 0xeb, 0x6a,
|
||||
0xdc, 0x29, 0x00, 0x7b, 0x29, 0xe7, 0xd3, 0x59, 0xf5, 0x75, 0x40, 0xa7,
|
||||
0x30, 0x9d, 0x80, 0xc5, 0x1b, 0x55, 0xa4, 0xd3, 0x85, 0xa5, 0x88, 0xf4,
|
||||
0x56, 0xce, 0xb4, 0x4a, 0x6d, 0x42, 0xeb, 0xe8, 0x89, 0x7b, 0x75, 0x53,
|
||||
0x28, 0xef, 0x7a, 0x13, 0x47, 0x5f, 0xbd, 0x01, 0xa0, 0x1c, 0x6e, 0xda,
|
||||
0x93, 0x1b, 0x73, 0x72, 0x23, 0x87, 0x1b, 0x2c, 0x89, 0x56, 0xbb, 0x0b,
|
||||
0xb2, 0xe1, 0x29, 0x35, 0x62, 0x33, 0x2c, 0x00, 0x12, 0x05, 0x7a, 0x2e,
|
||||
0xdf, 0x80, 0x34, 0xd2, 0xa7, 0x93, 0xc1, 0xaf, 0x07, 0xf2, 0x13, 0x0a,
|
||||
0xe0, 0xfc, 0xf5, 0x59, 0x7d, 0xf7, 0xf8, 0x62, 0xd9, 0xa7, 0xb3, 0xda,
|
||||
0xae, 0x84, 0xf5, 0x90, 0xd6, 0xf0, 0xad, 0x5a, 0x55, 0xb3, 0x9b, 0x2a,
|
||||
0x60, 0x60, 0x3a, 0x4c, 0x55, 0xcf, 0xa9, 0x02, 0x03, 0xda, 0x4d, 0xd5,
|
||||
0x11, 0x0f, 0x5e, 0x23, 0xda, 0x11, 0x28, 0x4a, 0xf3, 0x22, 0x4d, 0x09,
|
||||
0x6b, 0x81, 0x16, 0x4f, 0x75, 0x11, 0x71, 0xbe, 0x03, 0xf8, 0xda, 0xdd,
|
||||
0xeb, 0xb9, 0xc5, 0x7a, 0x3c, 0x81, 0x16, 0xac, 0xbc, 0x6e, 0x37, 0x55,
|
||||
0x6f, 0x30, 0xf0, 0x38, 0x55, 0xe6, 0x70, 0xec, 0x18, 0x67, 0x2a, 0xbb,
|
||||
0xc3, 0x9c, 0x1f, 0x7b, 0x42, 0xfc, 0xe9, 0xd3, 0xaf, 0x17, 0xc3, 0x97,
|
||||
0x7f, 0x1a, 0xf3, 0x3b, 0x2c, 0xf8, 0x25, 0xa6, 0x22, 0xc4, 0x90, 0x3c,
|
||||
0xc6, 0xed, 0x8b, 0x2f, 0x16, 0xe0, 0x0b, 0x2b, 0xe8, 0x23, 0x2e, 0xa4,
|
||||
0x0f, 0x05, 0xf2, 0x0c, 0xe0, 0x05, 0x28, 0xd3, 0xd2, 0xc2, 0x80, 0xd9,
|
||||
0xfd, 0x7e, 0xa2, 0x1b, 0xa7, 0x4d, 0x9c, 0xdb, 0x3a, 0x68, 0xd3, 0x67,
|
||||
0xda, 0xa3, 0x0c, 0xf9, 0x79, 0x48, 0xf3, 0x8a, 0x0b, 0xe0, 0x0a, 0x33,
|
||||
0x03, 0x3e, 0x03, 0x23, 0x64, 0x93, 0x85, 0x94, 0x1d, 0xe4, 0x71, 0x03,
|
||||
0x35, 0xec, 0x50, 0xa6, 0x43, 0xe1, 0xce, 0x80, 0xb0, 0x01, 0x4c, 0x4e,
|
||||
0x27, 0x11, 0x93, 0xc2, 0x34, 0x91, 0x30, 0x3a, 0x9b, 0x32, 0x88, 0xe6,
|
||||
0xcb, 0x61, 0xf9, 0x51, 0x75, 0x0a, 0x97, 0x0a, 0x52, 0xab, 0xa0, 0x0b,
|
||||
0x00, 0x06, 0xba, 0x01, 0xfc, 0x38, 0x7d, 0x0a, 0xbb, 0xc3, 0x57, 0xd2,
|
||||
0xe9, 0xf1, 0xd5, 0x02, 0xb5, 0xcc, 0xb8, 0x8e, 0x8f, 0xd7, 0x2d, 0x66,
|
||||
0x41, 0x25, 0x3d, 0xe8, 0x35, 0x99, 0x1b, 0xfe, 0x67, 0xc2, 0x69, 0x2b,
|
||||
0x0a, 0x84, 0xa4, 0x71, 0xe0, 0x5c, 0x99, 0x8d, 0x43, 0xb8, 0x9e, 0xc5,
|
||||
0xc1, 0xda, 0xc8, 0x30, 0x21, 0x42, 0xa8, 0x7e, 0x03, 0x80, 0x06, 0xfd,
|
||||
0x87, 0x0a, 0x2e, 0x26, 0x29, 0x90, 0x10, 0x8e, 0xc1, 0xff, 0x21, 0x9f,
|
||||
0x6b, 0xac, 0x60, 0xf0, 0xb3, 0x0c, 0xbe, 0x73, 0x95, 0x87, 0x66, 0xb0,
|
||||
0xf1, 0xc2, 0xc3, 0x3a, 0x25, 0x3b, 0x8d, 0xc0, 0xc3, 0x66, 0x9d, 0x40,
|
||||
0x8c, 0xcd, 0x16, 0xdc, 0xd2, 0xc5, 0x0b, 0x30, 0x65, 0xb8, 0x24, 0xd3,
|
||||
0x0e, 0x4b, 0xba, 0x59, 0x83, 0x12, 0x8e, 0xf1, 0x00, 0x8c, 0xad, 0x1b,
|
||||
0x83, 0xe2, 0xa0, 0xc0, 0x2d, 0xca, 0xe7, 0xf6, 0x01, 0x0f, 0xca, 0xe8,
|
||||
0x09, 0x0f, 0xc0, 0x5a, 0xf5, 0x01, 0x78, 0x1e, 0xe8, 0xb6, 0xbb, 0x21,
|
||||
0xc0, 0x82, 0xbd, 0x80, 0xaa, 0xc1, 0xe5, 0xc0, 0xcc, 0xcd, 0xb2, 0xeb,
|
||||
0x80, 0x27, 0x28, 0xe3, 0xc0, 0xf4, 0x50, 0x27, 0x66, 0xa6, 0x43, 0xdf,
|
||||
0xee, 0x40, 0x41, 0x7b, 0x90, 0x5a, 0x4e, 0x24, 0x8f, 0xa0, 0xff, 0xa1,
|
||||
0xa7, 0x14, 0x48, 0xcd, 0xc2, 0x60, 0x99, 0x95, 0xaa, 0x8c, 0x6a, 0xa9,
|
||||
0x6c, 0x7e, 0xad, 0xea, 0x08, 0x2f, 0x40, 0x21, 0x04, 0x76, 0x00, 0x42,
|
||||
0xd2, 0x1b, 0x55, 0x54, 0x55, 0x1c, 0x50, 0x6c, 0x43, 0x71, 0xf1, 0x2d,
|
||||
0x47, 0xb4, 0x06, 0x33, 0xc6, 0xa5, 0xcf, 0xea, 0x95, 0x38, 0xae, 0x8c,
|
||||
0xac, 0xe4, 0xd8, 0x63, 0xfa, 0x80, 0xf7, 0x0e, 0xcc, 0x16, 0x5a, 0xdd,
|
||||
0x6e, 0x30, 0xb7, 0xb7, 0x5d, 0xf3, 0x02, 0xa0, 0x99, 0x03, 0x83, 0x7c,
|
||||
0xd7, 0x3e, 0x42, 0x63, 0x6f, 0x7a, 0x32, 0x6c, 0xb7, 0xca, 0xf0, 0xd7,
|
||||
0xfa, 0x36, 0x09, 0xdd, 0x80, 0x66, 0x50, 0x16, 0x6b, 0x40, 0x02, 0xc1,
|
||||
0xfc, 0x22, 0x0c, 0x0a, 0xc6, 0x0a, 0xc5, 0x82, 0xc6, 0x41, 0xd2, 0xb8,
|
||||
0xf4, 0x8a, 0x82, 0xb0, 0x3a, 0x60, 0xb4, 0x26, 0x5c, 0xe3, 0x05, 0x1f,
|
||||
0xdd, 0x0a, 0x2f, 0xa0, 0x8a, 0x0c, 0x4b, 0xb5, 0x86, 0xfe, 0xfa, 0x88,
|
||||
0x35, 0x6c, 0xb0, 0xa0, 0x47, 0x9c, 0x75, 0xc7, 0x85, 0x8e, 0x45, 0x41,
|
||||
0xff, 0x87, 0x0b, 0xb9, 0xf8, 0x7b, 0xb9, 0xcf, 0x2d, 0xcc, 0x1a, 0x60,
|
||||
0xdb, 0x3e, 0x61, 0x6e, 0x61, 0xd6, 0xe6, 0xa9, 0xd9, 0x16, 0x13, 0xb0,
|
||||
0x8a, 0xd6, 0xa8, 0xb2, 0x3e, 0x5a, 0x61, 0x46, 0xc3, 0x95, 0xd4, 0xb7,
|
||||
0xd0, 0x30, 0x20, 0x83, 0x68, 0x42, 0x07, 0x6e, 0x35, 0xb4, 0x78, 0x2b,
|
||||
0xcc, 0x53, 0xfb, 0x82, 0xb7, 0x6e, 0x23, 0x40, 0x80, 0xca, 0xee, 0x45,
|
||||
0xfd, 0x04, 0xa1, 0xb7, 0x97, 0xe8, 0x34, 0xd5, 0x47, 0x0d, 0xf1, 0x6c,
|
||||
0xd1, 0x7b, 0x90, 0x1f, 0x2c, 0x0d, 0xa2, 0x03, 0x49, 0x8a, 0xe7, 0x80,
|
||||
0x8a, 0x0a, 0x51, 0xd1, 0x0d, 0x58, 0x41, 0xb3, 0x89, 0xe6, 0xd6, 0xc5,
|
||||
0x6d, 0x09, 0xfb, 0x80, 0x25, 0x2c, 0xab, 0xac, 0x3e, 0x1b, 0x2d, 0xc3,
|
||||
0x9b, 0x1b, 0x2e, 0x13, 0xb3, 0xa3, 0x05, 0x1f, 0xdc, 0xbc, 0x5f, 0x01,
|
||||
0x61, 0x0b, 0x6c, 0x21, 0x4c, 0xb3, 0x5f, 0x55, 0xe3, 0x89, 0xc1, 0x35,
|
||||
0x15, 0x10, 0x39, 0x8f, 0x0e, 0x95, 0x60, 0xa6, 0x88, 0xae, 0x14, 0x7b,
|
||||
0x52, 0xef, 0x5c, 0xe6, 0xf0, 0x2c, 0x20, 0x00, 0x0c, 0xea, 0x36, 0x02,
|
||||
0x33, 0xdb, 0xea, 0x77, 0x2a, 0xd4, 0xfd, 0xec, 0x1b, 0x20, 0x6b, 0x6d,
|
||||
0x47, 0x1b, 0xad, 0x5d, 0x54, 0x4f, 0x56, 0x1a, 0x15, 0xf4, 0x5b, 0x8e,
|
||||
0x21, 0xd5, 0x40, 0xbd, 0xf7, 0x0f, 0xd2, 0xcf, 0x27, 0xff, 0x3e, 0x2e,
|
||||
0x7c, 0xf2, 0xda, 0x47, 0x47, 0x04, 0x82, 0x41, 0xf5, 0xa0, 0xce, 0x30,
|
||||
0x0b, 0x36, 0x01, 0xd8, 0xfc, 0x79, 0x16, 0xed, 0xbe, 0x97, 0x35, 0x6d,
|
||||
0xff, 0x14, 0xa2, 0x7b, 0xa7, 0xd4, 0x47, 0x59, 0xd4, 0x92, 0x20, 0x27,
|
||||
0x33, 0x01, 0x3d, 0x72, 0x83, 0x5f, 0x01, 0xfc, 0x43, 0xe7, 0xa1, 0x87,
|
||||
0x50, 0x78, 0x4c, 0xa4, 0x7b, 0xa0, 0x07, 0x63, 0x54, 0x6f, 0x28, 0xc6,
|
||||
0x8e, 0xcb, 0x9c, 0xaa, 0x00, 0x68, 0xb3, 0x59, 0x09, 0x2c, 0x34, 0x02,
|
||||
0xfc, 0xd1, 0xec, 0x2a, 0xa1, 0x24, 0x67, 0xaa, 0x8c, 0x5f, 0xd3, 0x5e,
|
||||
0x89, 0x56, 0xf6, 0x8a, 0x6d, 0x9f, 0x07, 0xaa, 0x8a, 0x7f, 0x18, 0x2c,
|
||||
0x29, 0xcc, 0x54, 0x9e, 0x1f, 0x45, 0xc6, 0x4e, 0xe6, 0x33, 0x79, 0xcc,
|
||||
0xe8, 0x8a, 0xf9, 0x8d, 0xb8, 0xfe, 0x71, 0x1d, 0xdf, 0xf3, 0xe8, 0xbd,
|
||||
0xe3, 0x4f, 0x57, 0x78, 0xfe, 0xe8, 0x0f, 0x1e, 0x4d, 0x69, 0xe1, 0xb6,
|
||||
0xb8, 0xe4, 0x7b, 0x90, 0xb0, 0xab, 0x1f, 0xb8, 0x1d, 0xe9, 0x8d, 0xdf,
|
||||
0xc0, 0x4b, 0x98, 0x0a, 0x5c, 0xf6, 0x04, 0x44, 0x23, 0x44, 0x51, 0xac,
|
||||
0x15, 0x4a, 0xec, 0x23, 0x26, 0x13, 0x5c, 0x12, 0xfe, 0x3b, 0x40, 0x1e,
|
||||
0x90, 0x48, 0x07, 0x98, 0x5e, 0x30, 0x30, 0xff, 0x0a, 0xb7, 0xfe, 0x1e,
|
||||
0xbc, 0x1a, 0x6f, 0xf9, 0xde, 0xad, 0x07, 0xc5, 0xa8, 0xdb, 0xfe, 0x14,
|
||||
0x77, 0x14, 0x43, 0x1c, 0x17, 0x38, 0x96, 0x2f, 0x98, 0xcc, 0x16, 0x4b,
|
||||
0xdb, 0xf7, 0xfe, 0x0e, 0x6f, 0xa1, 0xfd, 0x56, 0x1d, 0x6f, 0x56, 0x48,
|
||||
0xd7, 0xdd, 0x2f, 0xd1, 0x1d, 0x6d, 0xcb, 0x66, 0xb0, 0x16, 0xd7, 0x83,
|
||||
0xa4, 0xee, 0x35, 0x09, 0x88, 0x26, 0x07, 0x44, 0xfb, 0xce, 0xe5, 0xf1,
|
||||
0xc3, 0xff, 0xce, 0x0f, 0xb2, 0x80, 0x29, 0x18, 0xe6, 0x1a, 0x6c, 0x73,
|
||||
0x0b, 0x3b, 0x5d, 0x02, 0xa0, 0x1c, 0x19, 0xa0, 0x64, 0x06, 0xd0, 0x18,
|
||||
0x91, 0x81, 0x59, 0x9d, 0x62, 0x82, 0x91, 0xe1, 0xb7, 0x50, 0xce, 0x12,
|
||||
0x80, 0xce, 0x53, 0x8c, 0x78, 0x8d, 0xc1, 0x23, 0xc3, 0x8d, 0x41, 0x82,
|
||||
0x16, 0x53, 0x69, 0x36, 0x6b, 0x89, 0x2f, 0xdb, 0x97, 0xe4, 0xc7, 0xe1,
|
||||
0xd2, 0x4e, 0xa7, 0x8b, 0xc9, 0x7a, 0xbd, 0x59, 0x6e, 0xb7, 0xdb, 0x3c,
|
||||
0x1e, 0x8f, 0x2f, 0xdc, 0x74, 0x7a, 0x73, 0x7d, 0xbf, 0x5e, 0x3d, 0x3d,
|
||||
0x6e, 0xf3, 0x30, 0x8c, 0xc7, 0xde, 0xbb, 0xe9, 0xf5, 0xfc, 0x7e, 0xbd,
|
||||
0x7c, 0x5a, 0x6e, 0x73, 0x40, 0x11, 0x57, 0x90, 0x21, 0xd7, 0xeb, 0x5e,
|
||||
0x90, 0xe1, 0xd3, 0x78, 0x0c, 0x12, 0x34, 0x9d, 0x48, 0x0b, 0x03, 0x3e,
|
||||
0xdb, 0x32, 0x9e, 0x31, 0x86, 0x5c, 0x17, 0x57, 0xa5, 0xdd, 0xc0, 0x3e,
|
||||
0xbf, 0x30, 0x88, 0x34, 0x86, 0xb1, 0x96, 0x0e, 0x04, 0xac, 0xf7, 0x70,
|
||||
0x18, 0x6c, 0x84, 0x47, 0xc5, 0x54, 0xf6, 0x49, 0x6a, 0xea, 0x34, 0xc5,
|
||||
0x7d, 0xea, 0x35, 0xa5, 0x7d, 0xca, 0x9a, 0xd4, 0xe5, 0x29, 0x45, 0xd3,
|
||||
0x38, 0x94, 0xa0, 0xc9, 0x6b, 0x24, 0xcf, 0xed, 0x93, 0xd5, 0xd4, 0x6a,
|
||||
0x6a, 0xf6, 0x49, 0xb9, 0x6f, 0x39, 0x32, 0xe7, 0xda, 0xb4, 0xaf, 0xe9,
|
||||
0x35, 0x92, 0x64, 0x45, 0x92, 0xf0, 0x54, 0x91, 0x64, 0x91, 0xce, 0x14,
|
||||
0xe9, 0x04, 0x49, 0xbc, 0xfc, 0xb4, 0x47, 0xaa, 0xf2, 0x31, 0xcc, 0x07,
|
||||
0x97, 0xab, 0x39, 0x9d, 0xa9, 0x8d, 0x57, 0xdc, 0xb7, 0xc4, 0x2c, 0xf5,
|
||||
0xce, 0x40, 0x25, 0xdf, 0x5a, 0x1d, 0x5a, 0x83, 0x50, 0xad, 0xce, 0xf3,
|
||||
0x50, 0x55, 0x3f, 0x2a, 0xf3, 0x3f, 0x18, 0x8d, 0x87, 0x9d, 0xd1, 0x68,
|
||||
0xe8, 0x95, 0xe3, 0x9d, 0x6a, 0xd7, 0xb2, 0xd6, 0x96, 0xc9, 0x0e, 0x45,
|
||||
0x9d, 0x4d, 0x28, 0xdd, 0x9f, 0xbd, 0xf9, 0xff, 0x21, 0x41, 0x79, 0x5f,
|
||||
0x82, 0x51, 0xe3, 0xbb, 0xe8, 0x70, 0xb3, 0x3b, 0x31, 0xaa, 0x68, 0x18,
|
||||
0x8d, 0x62, 0xc4, 0xb7, 0x31, 0xa7, 0xa4, 0x48, 0xf6, 0xa4, 0x68, 0x26,
|
||||
0xeb, 0x03, 0x29, 0xd2, 0xea, 0x3b, 0x88, 0xb2, 0x39, 0xd2, 0x1b, 0x0a,
|
||||
0x7e, 0xb1, 0xe7, 0x4b, 0x80, 0x2b, 0xac, 0x32, 0x9d, 0x96, 0xeb, 0xea,
|
||||
0x62, 0x41, 0x17, 0x41, 0xc5, 0x7a, 0x85, 0xab, 0x0a, 0x31, 0xb3, 0x03,
|
||||
0xe6, 0xf4, 0xe1, 0x8e, 0xb1, 0x9f, 0x4e, 0x6c, 0x72, 0x4b, 0x05, 0x39,
|
||||
0x0f, 0x02, 0x06, 0xe3, 0x2b, 0xf0, 0x9b, 0x6b, 0x85, 0xaf, 0x80, 0x47,
|
||||
0x63, 0x2c, 0xb9, 0x7a, 0x66, 0xa9, 0x2a, 0xc2, 0x75, 0xf1, 0x4b, 0x54,
|
||||
0xa6, 0x0e, 0x96, 0x9b, 0xe7, 0xf6, 0x89, 0x0e, 0x57, 0x87, 0xde, 0xfe,
|
||||
0xda, 0x56, 0xc3, 0x16, 0x7a, 0x8d, 0x9d, 0xd7, 0xc8, 0x86, 0x2b, 0xfb,
|
||||
0x78, 0x06, 0x15, 0x4a, 0x76, 0x19, 0xf1, 0x24, 0xe8, 0xe1, 0x13, 0x03,
|
||||
0xe8, 0xb0, 0x83, 0x30, 0x69, 0xaa, 0x4c, 0x70, 0x4f, 0x19, 0x20, 0x83,
|
||||
0x14, 0x80, 0x9c, 0xf4, 0xb9, 0x6f, 0x72, 0x58, 0xa9, 0x91, 0x1c, 0x9c,
|
||||
0x86, 0x71, 0x30, 0xe3, 0x70, 0x19, 0x76, 0xea, 0xf9, 0x95, 0xe3, 0x10,
|
||||
0x24, 0xfa, 0xf7, 0xa7, 0x9f, 0x65, 0x1d, 0xff, 0xc4, 0xa3, 0x66, 0xa7,
|
||||
0x18, 0xdf, 0x5c, 0xd4, 0x1d, 0xa1, 0x1e, 0x9e, 0x80, 0x04, 0xfa, 0xcc,
|
||||
0x86, 0xac, 0x7b, 0x0d, 0xb5, 0xf2, 0x1d, 0x3c, 0x00, 0x9c, 0xa2, 0xbb,
|
||||
0x1b, 0x46, 0x8c, 0x2b, 0xcd, 0x06, 0x3a, 0x01, 0x3c, 0x41, 0x7d, 0xe8,
|
||||
0x00, 0x10, 0x56, 0xbb, 0xd2, 0x6a, 0x88, 0x5e, 0xe0, 0x13, 0xd1, 0x05,
|
||||
0x63, 0x84, 0x1d, 0xed, 0xc2, 0xd5, 0x49, 0xe8, 0x08, 0x3c, 0x89, 0x0c,
|
||||
0x68, 0x9b, 0xf6, 0xcd, 0x16, 0x6c, 0x75, 0x1a, 0xd5, 0xd3, 0x58, 0xc4,
|
||||
0xd1, 0x8b, 0x86, 0xb3, 0x6a, 0x74, 0x5e, 0x15, 0x6a, 0xa7, 0x0c, 0x56,
|
||||
0x74, 0x97, 0x83, 0xbe, 0x4b, 0xa8, 0x6a, 0x00, 0xe5, 0xe1, 0x89, 0xbe,
|
||||
0x4d, 0x61, 0x3c, 0x89, 0xb1, 0x3b, 0x0d, 0x4d, 0xd0, 0x7d, 0x47, 0xb3,
|
||||
0x0c, 0x3a, 0x6e, 0x19, 0x75, 0x5c, 0x33, 0xec, 0x08, 0xf7, 0xae, 0x63,
|
||||
0x28, 0x63, 0x9f, 0xe5, 0x13, 0xda, 0x96, 0xd9, 0xe0, 0x61, 0x1a, 0x52,
|
||||
0x81, 0x8d, 0xb8, 0x83, 0xb1, 0x40, 0xe7, 0x98, 0x05, 0xf3, 0x91, 0x0a,
|
||||
0xec, 0xc8, 0x1d, 0x0c, 0x4a, 0x68, 0x1f, 0x9f, 0x56, 0xd9, 0xdd, 0xcf,
|
||||
0xe1, 0xce, 0xb9, 0x0b, 0x78, 0x76, 0x03, 0x9c, 0xbc, 0xa7, 0x27, 0xf8,
|
||||
0x7b, 0xf3, 0xf9, 0xbc, 0x0f, 0xde, 0xc1, 0xeb, 0xcb, 0x97, 0x43, 0xf3,
|
||||
0xbc, 0xdc, 0x24, 0x8b, 0x95, 0x15, 0x3d, 0x04, 0x89, 0x7e, 0x07, 0x2c,
|
||||
0xd6, 0x9e, 0xd1, 0x28, 0xa8, 0xb0, 0x1b, 0xa0, 0xc0, 0x70, 0xff, 0x31,
|
||||
0x27, 0x91, 0x33, 0x02, 0x85, 0x77, 0x20, 0xe3, 0x18, 0x81, 0x43, 0x56,
|
||||
0x17, 0xfa, 0x06, 0xe0, 0x0f, 0xb2, 0x07, 0x92, 0x07, 0xa6, 0x90, 0xe8,
|
||||
0xcd, 0xc7, 0x46, 0x7d, 0xf8, 0xd4, 0x68, 0x98, 0x80, 0xb1, 0x1c, 0x0f,
|
||||
0xf2, 0x61, 0x1f, 0x92, 0xba, 0x3e, 0xf7, 0xea, 0x43, 0x81, 0x27, 0x34,
|
||||
0xd9, 0x33, 0x78, 0x0f, 0x8f, 0x92, 0x82, 0x86, 0xc3, 0x0f, 0xd1, 0x62,
|
||||
0x85, 0xb6, 0x90, 0x69, 0x80, 0x4c, 0xd5, 0x11, 0xd8, 0x0b, 0x73, 0xb4,
|
||||
0x97, 0x62, 0x7f, 0x10, 0x1f, 0x61, 0x32, 0x72, 0x09, 0x32, 0x53, 0x37,
|
||||
0x45, 0x52, 0x0d, 0x5b, 0xa6, 0xba, 0x70, 0x32, 0xdd, 0xab, 0x16, 0xec,
|
||||
0x17, 0xac, 0xc4, 0x2e, 0x0b, 0x9d, 0x3e, 0xc7, 0x00, 0x22, 0xa0, 0x22,
|
||||
0x86, 0x09, 0xf0, 0xc0, 0x77, 0xea, 0xcf, 0xa1, 0x77, 0xf7, 0x89, 0x70,
|
||||
0xe3, 0x1e, 0xe8, 0xc0, 0x71, 0x63, 0x45, 0xb9, 0x3e, 0x34, 0x64, 0xe7,
|
||||
0x9b, 0x55, 0x18, 0x21, 0x7f, 0x51, 0x70, 0x51, 0xc8, 0xe1, 0xbe, 0x8a,
|
||||
0x53, 0x8f, 0x8d, 0x8e, 0x25, 0xd6, 0x33, 0x03, 0x0e, 0x96, 0x35, 0x0c,
|
||||
0xb5, 0xf9, 0x3d, 0x18, 0x8c, 0xd8, 0xbb, 0x1d, 0x4e, 0xec, 0x7a, 0xfd,
|
||||
0x01, 0x4b, 0xfe, 0x8e, 0x47, 0x81, 0xf1, 0xde, 0x8d, 0x46, 0x80, 0xf5,
|
||||
0xb2, 0x67, 0x14, 0x4d, 0xc3, 0x64, 0x3e, 0x8e, 0x34, 0x52, 0xa6, 0x41,
|
||||
0x38, 0x46, 0xd9, 0x18, 0x53, 0x63, 0x04, 0x8d, 0xc1, 0x33, 0x06, 0xd1,
|
||||
0x34, 0x24, 0xa7, 0x01, 0xb8, 0x5a, 0xba, 0x7c, 0x58, 0x3a, 0x1f, 0x4b,
|
||||
0xbf, 0xc3, 0x56, 0x75, 0xad, 0xf8, 0xee, 0xff, 0xe0, 0x54, 0xeb, 0x8f,
|
||||
0xe8, 0x42, 0x57, 0xb7, 0x23, 0xea, 0x5e, 0x06, 0xae, 0x7d, 0xdd, 0xe5,
|
||||
0xb0, 0x75, 0x0f, 0x02, 0xd7, 0xe3, 0xdd, 0x7e, 0xc4, 0xa1, 0xe0, 0xf0,
|
||||
0x7d, 0x05, 0xe3, 0xeb, 0x82, 0xdd, 0x88, 0xaa, 0xb6, 0xc3, 0xd6, 0xa1,
|
||||
0x65, 0x30, 0xe5, 0xe7, 0x9f, 0x76, 0xf5, 0x47, 0x2c, 0xfa, 0x91, 0xc6,
|
||||
0xfc, 0x29, 0x6e, 0xa8, 0x6a, 0x82, 0xfa, 0x36, 0x9b, 0xbe, 0x7d, 0x88,
|
||||
0x00, 0x12, 0xef, 0xba, 0x82, 0xd5, 0xea, 0x5b, 0x2e, 0xea, 0xe2, 0xba,
|
||||
0x01, 0xef, 0x96, 0xe6, 0x19, 0xe6, 0x98, 0x30, 0x74, 0x7e, 0x0c, 0x02,
|
||||
0xe5, 0x26, 0x00, 0xb4, 0x69, 0xdc, 0xc6, 0xc1, 0x5e, 0x25, 0xae, 0x50,
|
||||
0xc6, 0x2f, 0x5a, 0x8d, 0x2a, 0xcb, 0xed, 0x3a, 0xc3, 0x6a, 0x49, 0x98,
|
||||
0xbc, 0x14, 0xc0, 0xa1, 0x4e, 0x2d, 0x17, 0x26, 0xa3, 0x97, 0x9e, 0x5e,
|
||||
0xad, 0x7b, 0x78, 0x2c, 0x30, 0x87, 0xe2, 0x77, 0x05, 0x16, 0x51, 0x9d,
|
||||
0x5d, 0x4b, 0x43, 0xb8, 0x94, 0xb0, 0x50, 0x6d, 0x5b, 0xcc, 0x4b, 0x70,
|
||||
0x8f, 0x40, 0x97, 0xf4, 0xd7, 0x5d, 0x38, 0x6d, 0x17, 0x48, 0x4b, 0x54,
|
||||
0x12, 0x54, 0x1a, 0xba, 0xaf, 0x64, 0xdf, 0xe7, 0x70, 0x9b, 0x35, 0x0a,
|
||||
0x0d, 0x57, 0x7a, 0x33, 0xc0, 0xf0, 0xc1, 0x03, 0x81, 0x7d, 0x67, 0x6c,
|
||||
0x0e, 0xe6, 0x0b, 0x46, 0x0e, 0xc4, 0x35, 0x6b, 0x24, 0x0f, 0x53, 0x1e,
|
||||
0xb9, 0xe5, 0xfa, 0x95, 0x6c, 0x46, 0x5e, 0x05, 0xcc, 0xa0, 0x7d, 0x16,
|
||||
0xb7, 0x51, 0x8e, 0x35, 0x05, 0x13, 0x58, 0x0c, 0xdc, 0x8e, 0xfd, 0x0f,
|
||||
0x2e, 0x2c, 0xcf, 0xb8, 0xab, 0xe5, 0x68, 0xb7, 0x82, 0x35, 0x4b, 0x6c,
|
||||
0x0c, 0x80, 0x64, 0x1d, 0x41, 0x1d, 0x4c, 0xd5, 0xf7, 0x3a, 0xf2, 0xe3,
|
||||
0xe5, 0xa1, 0x00, 0x07, 0xce, 0xd7, 0x4a, 0xb8, 0xcf, 0xca, 0xe0, 0xb4,
|
||||
0xf9, 0x08, 0xe0, 0x6b, 0x40, 0xbf, 0xbe, 0x73, 0xb0, 0x33, 0x74, 0xa8,
|
||||
0x88, 0x63, 0xc8, 0x05, 0x0e, 0xeb, 0x73, 0x71, 0x9b, 0xec, 0x8e, 0x03,
|
||||
0xbe, 0x62, 0x3f, 0xc0, 0x9a, 0xc9, 0x0a, 0x1d, 0xdb, 0x05, 0x17, 0x8a,
|
||||
0x1a, 0x30, 0x3e, 0xcb, 0xde, 0x97, 0xe6, 0x80, 0xc5, 0x3e, 0xab, 0xe0,
|
||||
0xd6, 0x0c, 0x09, 0x03, 0xaa, 0xc4, 0xc4, 0x3a, 0xda, 0x11, 0x49, 0x38,
|
||||
0x46, 0xfb, 0xb8, 0x1b, 0x6d, 0x39, 0x1f, 0xed, 0xa9, 0x72, 0x9c, 0x0f,
|
||||
0x7c, 0x37, 0xda, 0x81, 0xa3, 0x5d, 0xbc, 0x33, 0x5a, 0x54, 0xd0, 0xe2,
|
||||
0xcf, 0xf2, 0x2f, 0x1d, 0xff, 0x4e, 0x6b, 0x3d, 0xcd, 0xff, 0x23, 0x7f,
|
||||
0xa8, 0x1d, 0xe6, 0xe4, 0xb7, 0x46, 0xe0, 0xdf, 0x28, 0x11, 0x84, 0xc9,
|
||||
0xee, 0xab, 0xfd, 0xed, 0x7d, 0xdc, 0xaf, 0xff, 0xf6, 0x68, 0x7f, 0xf7,
|
||||
0x68, 0x30, 0x2b, 0x2c, 0xe2, 0x96, 0x34, 0xee, 0xe4, 0x00, 0xa4, 0xc3,
|
||||
0x44, 0x58, 0xda, 0x86, 0xca, 0x33, 0x49, 0x37, 0x69, 0x4f, 0x89, 0xdd,
|
||||
0xd4, 0x6b, 0x06, 0xda, 0x61, 0xbf, 0xa8, 0x50, 0xd9, 0x99, 0x30, 0xe9,
|
||||
0x47, 0x79, 0x6c, 0xdc, 0x3d, 0x4e, 0xc5, 0xb8, 0x1b, 0x92, 0x41, 0x65,
|
||||
0x92, 0x0c, 0x6b, 0x90, 0x97, 0x82, 0xea, 0xd2, 0x59, 0x79, 0xa5, 0x1f,
|
||||
0xef, 0x5e, 0x8a, 0x9f, 0x17, 0x09, 0xcf, 0x55, 0x1a, 0x5b, 0x92, 0xf2,
|
||||
0x53, 0x65, 0xfb, 0xc1, 0xcb, 0x7a, 0x9c, 0x06, 0xe4, 0x3f, 0x38, 0x4e,
|
||||
0xf7, 0xae, 0xf7, 0x71, 0xea, 0x6f, 0x6c, 0x71, 0xbf, 0x7b, 0xb3, 0xe4,
|
||||
0x94, 0xdc, 0xbe, 0xc8, 0x43, 0x09, 0xab, 0xd2, 0x76, 0x67, 0x9b, 0x0e,
|
||||
0x89, 0x8a, 0xfe, 0x66, 0xa7, 0xe9, 0xf8, 0x80, 0x8e, 0x20, 0xf7, 0xb3,
|
||||
0xa0, 0xe7, 0x20, 0xe2, 0xa8, 0x17, 0x42, 0xba, 0xce, 0xa8, 0x95, 0xfb,
|
||||
0x2b, 0xba, 0x17, 0xbc, 0xdf, 0x80, 0xda, 0x6f, 0xee, 0x42, 0x8b, 0x6f,
|
||||
0x3b, 0x59, 0x80, 0xc1, 0x25, 0x7f, 0x97, 0x61, 0x60, 0xf1, 0x86, 0x6e,
|
||||
0xb6, 0xd7, 0x4b, 0x86, 0x42, 0x41, 0xd2, 0x50, 0x15, 0xb0, 0x09, 0x6a,
|
||||
0x6e, 0xeb, 0x9e, 0xc7, 0xb7, 0x25, 0xf2, 0xea, 0x30, 0x4d, 0xf7, 0xc9,
|
||||
0x3e, 0x16, 0x78, 0x1a, 0x21, 0x71, 0xc7, 0xf4, 0xb6, 0x1c, 0x9f, 0x4d,
|
||||
0x84, 0x32, 0xa7, 0xf4, 0x81, 0x37, 0x64, 0x05, 0xff, 0xee, 0x8b, 0x24,
|
||||
0xf0, 0x6f, 0xe4, 0x61, 0x43, 0xd7, 0x91, 0xc7, 0x94, 0x7e, 0xe3, 0x4f,
|
||||
0x51, 0x22, 0x5e, 0xa6, 0x1d, 0x58, 0x9c, 0x42, 0xe3, 0x82, 0xd0, 0xf8,
|
||||
0x47, 0xea, 0xd9, 0x83, 0x85, 0x1c, 0xc1, 0x02, 0x0f, 0xfb, 0x0a, 0x16,
|
||||
0x82, 0xb9, 0x62, 0xde, 0x5a, 0x51, 0x79, 0x91, 0xe4, 0xf6, 0x04, 0xac,
|
||||
0xbf, 0x81, 0x74, 0x35, 0xfb, 0xa4, 0x34, 0xe1, 0x32, 0x87, 0x05, 0x23,
|
||||
0x97, 0x80, 0xd0, 0x2c, 0x01, 0x66, 0x69, 0x32, 0xfc, 0xe5, 0x43, 0xdc,
|
||||
0x35, 0x76, 0x35, 0x84, 0x87, 0xa7, 0x1d, 0x19, 0xa2, 0x1b, 0xf0, 0x43,
|
||||
0x52, 0x03, 0x22, 0xec, 0x98, 0x95, 0x7f, 0xa6, 0x79, 0xf9, 0xe5, 0x43,
|
||||
0x20, 0x80, 0x5c, 0x6f, 0x30, 0xee, 0x07, 0xbe, 0xc2, 0x63, 0x05, 0x0b,
|
||||
0x16, 0xc1, 0x1a, 0x7b, 0x38, 0x0b, 0x99, 0x7b, 0x38, 0x28, 0x05, 0x4f,
|
||||
0xf3, 0x9b, 0x19, 0x4a, 0xea, 0xa8, 0x15, 0x41, 0xee, 0x30, 0x84, 0xf5,
|
||||
0xb6, 0x58, 0x90, 0xf3, 0x30, 0x7a, 0x1a, 0x3e, 0xb9, 0xf4, 0x3b, 0xf7,
|
||||
0xe6, 0x49, 0x20, 0xc9, 0x10, 0x62, 0xbb, 0xbb, 0x34, 0xdc, 0xe3, 0x53,
|
||||
0xea, 0xd0, 0x77, 0xf2, 0x2b, 0x68, 0x81, 0x5b, 0x74, 0xa5, 0xb4, 0x1b,
|
||||
0x58, 0x7a, 0x86, 0x07, 0xee, 0x38, 0xa4, 0xe8, 0xb2, 0xf9, 0x0b, 0x58,
|
||||
0x1d, 0xa9, 0xb9, 0x72, 0x74, 0x90, 0xf5, 0x48, 0x8a, 0x6e, 0x4f, 0x76,
|
||||
0xcb, 0x4b, 0xcd, 0x78, 0x53, 0xe2, 0xbd, 0x8c, 0xa0, 0x2e, 0x82, 0xc5,
|
||||
0xd2, 0x81, 0x33, 0xe0, 0xd3, 0x4f, 0xd2, 0x54, 0x5e, 0xce, 0x74, 0xa3,
|
||||
0x82, 0xc7, 0x02, 0xd8, 0x8a, 0x65, 0x61, 0xd3, 0x7f, 0xa9, 0x35, 0xda,
|
||||
0x95, 0x84, 0x91, 0xdb, 0x0e, 0xf0, 0x7d, 0xc2, 0x74, 0x59, 0x02, 0x5d,
|
||||
0x1c, 0x38, 0x38, 0x76, 0x49, 0x46, 0x42, 0xaa, 0x04, 0xb7, 0x06, 0x4e,
|
||||
0x0d, 0x5c, 0x1a, 0xf8, 0x38, 0xcd, 0x13, 0x3f, 0xbe, 0xf1, 0xe8, 0x3d,
|
||||
0xdd, 0x18, 0x78, 0xd0, 0x70, 0x0e, 0x02, 0x7c, 0x15, 0x5c, 0xd2, 0x5d,
|
||||
0xf0, 0x47, 0xe9, 0xf1, 0x6b, 0x9f, 0xf1, 0xee, 0x17, 0xea, 0x2f, 0xec,
|
||||
0x7e, 0x63, 0x6f, 0xf3, 0xf9, 0xef, 0x26, 0x17, 0xfc, 0x06, 0x3f, 0xbd,
|
||||
0x1c, 0xdc, 0xeb, 0xdf, 0xff, 0xfc, 0x23, 0xbb, 0xf2, 0x8f, 0x6c, 0x8b,
|
||||
0x3f, 0xff, 0xc1, 0x39, 0xc5, 0x8f, 0x5b, 0x99, 0xa9, 0xdd, 0xff, 0x32,
|
||||
0x7f, 0x54, 0x00, 0x38, 0x93, 0x99, 0x1f, 0x67, 0x71, 0x67, 0xa9, 0xc0,
|
||||
0xb7, 0xc3, 0x18, 0xe0, 0xc9, 0x61, 0x44, 0x79, 0x0c, 0x1f, 0xc8, 0x3e,
|
||||
0xf1, 0x6b, 0x00, 0xc7, 0x30, 0x8c, 0xc6, 0xd1, 0x72, 0x9d, 0xf6, 0xba,
|
||||
0x5d, 0x09, 0x1f, 0x88, 0x23, 0x19, 0xb8, 0x91, 0x26, 0x4a, 0x0b, 0x51,
|
||||
0x74, 0xd1, 0xfb, 0xdb, 0x38, 0x26, 0x0f, 0x99, 0x64, 0xb8, 0xa8, 0xe2,
|
||||
0x80, 0xbb, 0x57, 0xa5, 0xc0, 0x2b, 0xa5, 0x17, 0x03, 0x37, 0x98, 0xb4,
|
||||
0x0b, 0x96, 0xd5, 0x93, 0x5c, 0x6a, 0x9a, 0x0d, 0xee, 0x90, 0xee, 0x06,
|
||||
0xbb, 0xca, 0x87, 0xd4, 0xae, 0x52, 0xbb, 0xd9, 0xa5, 0x86, 0x09, 0x76,
|
||||
0x15, 0xbe, 0x4d, 0x4d, 0xfb, 0x4f, 0x45, 0xb4, 0x37, 0xfb, 0xcf, 0x53,
|
||||
0x6a, 0xd2, 0x0d, 0x40, 0xa1, 0x3b, 0xe6, 0xf4, 0xab, 0x3f, 0xa7, 0x7e,
|
||||
0x85, 0xc3, 0xd2, 0xc5, 0xaa, 0xda, 0xd0, 0x5d, 0xe1, 0x45, 0x52, 0xdd,
|
||||
0xe7, 0x52, 0xa0, 0x1c, 0xce, 0x2f, 0xc5, 0x9c, 0x5d, 0x72, 0x9b, 0x8d,
|
||||
0x97, 0xa7, 0x47, 0xc2, 0xc4, 0x41, 0xb2, 0x00, 0xf4, 0xa5, 0xb4, 0xdf,
|
||||
0xf1, 0x51, 0x0f, 0x8c, 0x92, 0x16, 0xc3, 0xe2, 0x0c, 0xfc, 0xeb, 0x20,
|
||||
0x8a, 0x6f, 0x7c, 0xf2, 0x83, 0xbf, 0x06, 0x70, 0xd0, 0xa2, 0xa1, 0x26,
|
||||
0xff, 0x61, 0xa3, 0x23, 0x94, 0x77, 0xa4, 0x0b, 0x8d, 0x95, 0xa9, 0xd4,
|
||||
0xa7, 0x7d, 0xff, 0x2f, 0xef, 0xff, 0x1a, 0x10, 0x5f, 0x2e, 0x31, 0x36,
|
||||
0xdd, 0x94, 0x43, 0xfe, 0x07, 0xef, 0xb2, 0x66, 0x87, 0x36, 0xd0, 0x52,
|
||||
0x8f, 0xf6, 0xe8, 0xd3, 0xf8, 0xfe, 0xaf, 0x92, 0xea, 0x57, 0x4f, 0xb0,
|
||||
0x76, 0xff, 0xb2, 0xe5, 0x17, 0x7e, 0x60, 0xd4, 0xd7, 0x8f, 0x70, 0xe8,
|
||||
0xc9, 0x41, 0x67, 0x34, 0xae, 0x07, 0xd5, 0x02, 0x5e, 0x2d, 0xd1, 0x52,
|
||||
0x53, 0x2c, 0xbd, 0x61, 0x88, 0x9a, 0x3b, 0x9c, 0x98, 0x19, 0x2a, 0x6a,
|
||||
0x88, 0xee, 0x11, 0xb7, 0xf7, 0xa0, 0x5c, 0xdc, 0xb9, 0xa5, 0x3b, 0xaa,
|
||||
0x9e, 0x2d, 0x63, 0xe7, 0x91, 0xc1, 0x29, 0xba, 0xa6, 0xf4, 0x4b, 0xbf,
|
||||
0xf5, 0xf4, 0xb4, 0x2a, 0xd6, 0x9c, 0x43, 0x6d, 0x68, 0x52, 0x46, 0x68,
|
||||
0xd7, 0x32, 0x70, 0x01, 0x63, 0x48, 0x33, 0xa5, 0x11, 0xc2, 0x56, 0xc3,
|
||||
0x37, 0x35, 0x72, 0xa2, 0xc4, 0x8e, 0x5f, 0x9f, 0xea, 0x97, 0x4d, 0x40,
|
||||
0x48, 0x24, 0x8d, 0x40, 0x27, 0x4d, 0xfd, 0x18, 0x53, 0xbb, 0x4f, 0xe6,
|
||||
0xbd, 0x24, 0x04, 0x99, 0xb3, 0xf4, 0x41, 0xe1, 0x57, 0xc9, 0xf3, 0xab,
|
||||
0xc6, 0xf3, 0x64, 0x34, 0x36, 0xb9, 0x8f, 0x25, 0x45, 0x0d, 0x01, 0xd4,
|
||||
0x68, 0xc6, 0xa0, 0xd1, 0x23, 0xc6, 0x20, 0xa1, 0x40, 0x35, 0x6c, 0xa4,
|
||||
0x1f, 0x66, 0x6a, 0xbc, 0xa8, 0xdd, 0xd6, 0x78, 0xd1, 0x0b, 0xa4, 0xb4,
|
||||
0x88, 0x23, 0x39, 0x0f, 0x14, 0x1d, 0x23, 0x44, 0xf0, 0xc8, 0x35, 0xd2,
|
||||
0xd5, 0x9f, 0x9c, 0x9a, 0xb3, 0xe0, 0xea, 0x47, 0xcf, 0x8e, 0xa7, 0xff,
|
||||
0x05, 0xbc, 0x47, 0xa4, 0x19, 0x06, 0x2d, 0x00, 0x00
|
||||
};
|
||||
|
||||
const GFXglyph Oswald_Medium30pt7bGlyphs[] PROGMEM = {
|
||||
{ 0, 1, 1, 14, 0, 0 }, // 0x20 ' '
|
||||
{ 1, 8, 48, 14, 3, -47 }, // 0x21 '!'
|
||||
{ 49, 17, 17, 19, 1, -47 }, // 0x22 '"'
|
||||
{ 86, 26, 48, 30, 2, -47 }, // 0x23 '#'
|
||||
{ 242, 25, 59, 29, 2, -52 }, // 0x24 '$'
|
||||
{ 427, 51, 48, 56, 2, -47 }, // 0x25 '%'
|
||||
{ 733, 29, 49, 34, 3, -47 }, // 0x26 '&'
|
||||
{ 911, 7, 17, 9, 1, -47 }, // 0x27 '''
|
||||
{ 926, 13, 59, 19, 4, -47 }, // 0x28 '('
|
||||
{ 1022, 13, 59, 17, 2, -47 }, // 0x29 ')'
|
||||
{ 1118, 21, 21, 24, 2, -47 }, // 0x2A '*'
|
||||
{ 1174, 22, 24, 25, 2, -35 }, // 0x2B '+'
|
||||
{ 1240, 8, 16, 13, 2, -7 }, // 0x2C ','
|
||||
{ 1256, 14, 6, 18, 2, -19 }, // 0x2D '-'
|
||||
{ 1267, 8, 8, 13, 3, -7 }, // 0x2E '.'
|
||||
{ 1275, 19, 48, 23, 2, -47 }, // 0x2F '/'
|
||||
{ 1389, 25, 49, 31, 3, -47 }, // 0x30 '0'
|
||||
{ 1543, 15, 48, 22, 2, -47 }, // 0x31 '1'
|
||||
{ 1633, 25, 48, 29, 2, -47 }, // 0x32 '2'
|
||||
{ 1783, 25, 49, 29, 2, -47 }, // 0x33 '3'
|
||||
{ 1937, 27, 48, 30, 2, -47 }, // 0x34 '4'
|
||||
{ 2099, 24, 49, 29, 3, -47 }, // 0x35 '5'
|
||||
{ 2246, 25, 49, 31, 3, -47 }, // 0x36 '6'
|
||||
{ 2400, 21, 48, 24, 1, -47 }, // 0x37 '7'
|
||||
{ 2526, 24, 49, 30, 3, -47 }, // 0x38 '8'
|
||||
{ 2673, 25, 49, 31, 2, -47 }, // 0x39 '9'
|
||||
{ 2827, 7, 28, 14, 4, -31 }, // 0x3A ':'
|
||||
{ 2852, 8, 38, 15, 4, -32 }, // 0x3B ';'
|
||||
{ 2890, 18, 25, 23, 2, -36 }, // 0x3C '<'
|
||||
{ 2947, 19, 16, 25, 3, -31 }, // 0x3D '='
|
||||
{ 2985, 18, 25, 23, 3, -36 }, // 0x3E '>'
|
||||
{ 3042, 24, 48, 28, 2, -47 }, // 0x3F '?'
|
||||
{ 3186, 50, 56, 55, 3, -47 }, // 0x40 '@'
|
||||
{ 3536, 29, 48, 31, 1, -47 }, // 0x41 'A'
|
||||
{ 3710, 27, 48, 33, 4, -47 }, // 0x42 'B'
|
||||
{ 3872, 26, 49, 32, 3, -47 }, // 0x43 'C'
|
||||
{ 4032, 26, 48, 33, 4, -47 }, // 0x44 'D'
|
||||
{ 4188, 20, 48, 25, 4, -47 }, // 0x45 'E'
|
||||
{ 4308, 19, 48, 24, 4, -47 }, // 0x46 'F'
|
||||
{ 4422, 27, 49, 33, 3, -47 }, // 0x47 'G'
|
||||
{ 4588, 27, 48, 34, 4, -47 }, // 0x48 'H'
|
||||
{ 4750, 8, 48, 16, 4, -47 }, // 0x49 'I'
|
||||
{ 4798, 15, 49, 19, 1, -47 }, // 0x4A 'J'
|
||||
{ 4890, 27, 48, 31, 4, -47 }, // 0x4B 'K'
|
||||
{ 5052, 20, 48, 25, 4, -47 }, // 0x4C 'L'
|
||||
{ 5172, 34, 48, 40, 3, -47 }, // 0x4D 'M'
|
||||
{ 5376, 24, 48, 32, 4, -47 }, // 0x4E 'N'
|
||||
{ 5520, 27, 49, 33, 3, -47 }, // 0x4F 'O'
|
||||
{ 5686, 26, 48, 31, 4, -47 }, // 0x50 'P'
|
||||
{ 5842, 27, 57, 33, 3, -47 }, // 0x51 'Q'
|
||||
{ 6035, 27, 48, 33, 4, -47 }, // 0x52 'R'
|
||||
{ 6197, 26, 49, 29, 2, -47 }, // 0x53 'S'
|
||||
{ 6357, 24, 48, 25, 1, -47 }, // 0x54 'T'
|
||||
{ 6501, 27, 49, 33, 3, -47 }, // 0x55 'U'
|
||||
{ 6667, 28, 48, 30, 1, -47 }, // 0x56 'V'
|
||||
{ 6835, 38, 48, 42, 2, -47 }, // 0x57 'W'
|
||||
{ 7063, 28, 48, 29, 1, -47 }, // 0x58 'X'
|
||||
{ 7231, 27, 48, 29, 1, -47 }, // 0x59 'Y'
|
||||
{ 7393, 22, 48, 25, 2, -47 }, // 0x5A 'Z'
|
||||
// Euro sign ([) - ASCII code 91
|
||||
{ 11030, 30, 49, 31, 0, -47 }, // 0x5B '['
|
||||
// Backslash placeholder - ASCII code 92
|
||||
{ 0, 0, 0, 0, 0, 0 }, // 0x5C '\'
|
||||
// Pound sign (]) - ASCII code 93
|
||||
{ 11214, 24, 48, 26, 1, -47 }, // 0x5D ']'
|
||||
// Yen sign (^) - ASCII code 94
|
||||
{ 11358, 28, 48, 27, 0, -47 }, // 0x5E '^'
|
||||
{ 7905, 21, 6, 21, 0, 4 }, // 0x5F '_'
|
||||
{ 7921, 11, 12, 17, 3, -47 }, // 0x60 '`'
|
||||
{ 7938, 22, 35, 26, 1, -33 }, // 0x61 'a'
|
||||
{ 8035, 23, 49, 28, 3, -47 }, // 0x62 'b'
|
||||
{ 8176, 22, 35, 26, 2, -33 }, // 0x63 'c'
|
||||
{ 8273, 23, 49, 28, 2, -47 }, // 0x64 'd'
|
||||
{ 8414, 22, 35, 26, 2, -33 }, // 0x65 'e'
|
||||
{ 8511, 16, 46, 18, 1, -45 }, // 0x66 'f'
|
||||
{ 8603, 28, 46, 28, 1, -34 }, // 0x67 'g'
|
||||
{ 8764, 22, 48, 28, 3, -47 }, // 0x68 'h'
|
||||
{ 8896, 8, 46, 15, 3, -45 }, // 0x69 'i'
|
||||
{ 8942, 13, 56, 15, -1, -45 }, // 0x6A 'j'
|
||||
{ 9033, 25, 48, 28, 3, -47 }, // 0x6B 'k'
|
||||
{ 9183, 8, 48, 15, 4, -47 }, // 0x6C 'l'
|
||||
{ 9231, 36, 35, 42, 3, -34 }, // 0x6D 'm'
|
||||
{ 9389, 22, 34, 28, 3, -33 }, // 0x6E 'n'
|
||||
{ 9483, 22, 35, 27, 2, -33 }, // 0x6F 'o'
|
||||
{ 9580, 23, 45, 28, 3, -33 }, // 0x70 'p'
|
||||
{ 9710, 22, 45, 28, 3, -33 }, // 0x71 'q'
|
||||
{ 9834, 17, 34, 21, 3, -33 }, // 0x72 'r'
|
||||
{ 9907, 21, 35, 24, 1, -33 }, // 0x73 's'
|
||||
{ 9999, 17, 44, 19, 1, -43 }, // 0x74 't'
|
||||
{ 10093, 22, 35, 28, 3, -33 }, // 0x75 'u'
|
||||
{ 10190, 22, 34, 24, 1, -33 }, // 0x76 'v'
|
||||
{ 10284, 32, 34, 35, 1, -33 }, // 0x77 'w'
|
||||
{ 10420, 23, 34, 24, 1, -33 }, // 0x78 'x'
|
||||
{ 10518, 24, 43, 25, 0, -33 }, // 0x79 'y'
|
||||
{ 10647, 18, 34, 22, 2, -33 }, // 0x7A 'z'
|
||||
{ 10724, 15, 59, 20, 3, -47 }, // 0x7B '{'
|
||||
{ 10835, 7, 58, 15, 4, -47 }, // 0x7C '|'
|
||||
{ 10886, 16, 59, 21, 2, -47 }, // 0x7D '}'
|
||||
{ 11004, 23, 9, 27, 2, -28 } }; // 0x7E '~'
|
||||
|
||||
// const GFXfont Oswald_Medium30pt7b PROGMEM = {
|
||||
// (uint8_t *)Oswald_Medium30pt7bBitmaps,
|
||||
// (GFXglyph *)Oswald_Medium30pt7bGlyphs,
|
||||
// 0x20, 0x7E, 87 };
|
||||
|
||||
// Approx. 11702 bytes
|
||||
|
||||
|
||||
// Font properties
|
||||
static constexpr FontData Oswald_Medium30pt7b_Properties = {
|
||||
Oswald_Medium30pt7bBitmaps_Gzip,
|
||||
Oswald_Medium30pt7bGlyphs,
|
||||
sizeof(Oswald_Medium30pt7bBitmaps_Gzip),
|
||||
11526, // Original size
|
||||
0x20, // First char
|
||||
0x7E, // Last char
|
||||
87 // yAdvance
|
||||
};
|
File diff suppressed because it is too large
Load diff
1427
src/icons/icons.cpp
1427
src/icons/icons.cpp
File diff suppressed because it is too large
Load diff
|
@ -36,7 +36,7 @@ void taskBitaxeFetch(void *pvParameters)
|
|||
bitaxeHashrate = std::to_string(static_cast<int>(std::round(doc["hashRate"].as<float>())));
|
||||
bitaxeBestDiff = doc["bestDiff"].as<std::string>();
|
||||
|
||||
if (workQueue != nullptr && (ScreenHandler::getCurrentScreen() == SCREEN_BITAXE_HASHRATE || ScreenHandler::getCurrentScreen() == SCREEN_BITAXE_BESTDIFF))
|
||||
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BITAXE_HASHRATE || getCurrentScreen() == SCREEN_BITAXE_BESTDIFF))
|
||||
{
|
||||
WorkItem priceUpdate = {TASK_BITAXE_UPDATE, 0};
|
||||
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
||||
|
@ -54,7 +54,7 @@ void taskBitaxeFetch(void *pvParameters)
|
|||
|
||||
void setupBitaxeFetchTask()
|
||||
{
|
||||
xTaskCreate(taskBitaxeFetch, "bitaxeFetch", (3 * 1024), NULL, tskIDLE_PRIORITY,
|
||||
xTaskCreate(taskBitaxeFetch, "bitaxeFetch", (6 * 1024), NULL, tskIDLE_PRIORITY,
|
||||
&bitaxeFetchTaskHandle);
|
||||
|
||||
xTaskNotifyGive(bitaxeFetchTaskHandle);
|
||||
|
|
|
@ -82,6 +82,11 @@ void setupBlockNotify()
|
|||
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 +
|
||||
// "/api/v1/ws").c_str());
|
||||
|
||||
|
@ -195,7 +200,7 @@ void processNewBlock(uint newBlockHeight) {
|
|||
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
||||
// xTaskNotifyGive(blockUpdateTaskHandle);
|
||||
|
||||
if (ScreenHandler::getCurrentScreen() != SCREEN_BLOCK_HEIGHT &&
|
||||
if (getCurrentScreen() != SCREEN_BLOCK_HEIGHT &&
|
||||
preferences.getBool("stealFocus", DEFAULT_STEAL_FOCUS))
|
||||
{
|
||||
uint64_t timerPeriod = 0;
|
||||
|
@ -205,7 +210,7 @@ void processNewBlock(uint newBlockHeight) {
|
|||
timerPeriod = getTimerSeconds();
|
||||
esp_timer_stop(screenRotateTimer);
|
||||
}
|
||||
ScreenHandler::setCurrentScreen(SCREEN_BLOCK_HEIGHT);
|
||||
setCurrentScreen(SCREEN_BLOCK_HEIGHT);
|
||||
if (timerPeriod > 0)
|
||||
{
|
||||
esp_timer_start_periodic(screenRotateTimer,
|
||||
|
@ -291,26 +296,44 @@ void restartBlockNotify()
|
|||
}
|
||||
|
||||
|
||||
int getBlockFetch() {
|
||||
int getBlockFetch()
|
||||
{
|
||||
try {
|
||||
String mempoolInstance = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
||||
WiFiClientSecure client;
|
||||
|
||||
if (preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE)) {
|
||||
client.setCACertBundle(rootca_crt_bundle_start);
|
||||
}
|
||||
|
||||
String mempoolInstance =
|
||||
preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
||||
|
||||
// Get current block height through regular API
|
||||
HTTPClient http;
|
||||
|
||||
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 (preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE))
|
||||
http.begin(client, protocol + "://" + mempoolInstance + "/api/blocks/tip/height");
|
||||
else
|
||||
http.begin(protocol + "://" + mempoolInstance + "/api/blocks/tip/height");
|
||||
|
||||
if (httpCode > 0 && httpCode == HTTP_CODE_OK) {
|
||||
String blockHeightStr = http->getString();
|
||||
HttpHelper::end(http);
|
||||
Serial.println("Fetching block height from " + protocol + "://" + mempoolInstance + "/api/blocks/tip/height");
|
||||
int httpCode = http.GET();
|
||||
|
||||
if (httpCode > 0 && httpCode == HTTP_CODE_OK)
|
||||
{
|
||||
String blockHeightStr = http.getString();
|
||||
return blockHeightStr.toInt();
|
||||
}
|
||||
HttpHelper::end(http);
|
||||
} else {
|
||||
Serial.println("HTTP code" + String(httpCode));
|
||||
} catch (...) {
|
||||
Serial.println(F("An exception occurred while trying to get the latest block"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
Serial.println(F("An exception occured while trying to get the latest block"));
|
||||
}
|
||||
|
||||
return 2203; // B-T-C
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include "button_handler.hpp"
|
||||
|
||||
// Initialize static members
|
||||
TaskHandle_t ButtonHandler::buttonTaskHandle = NULL;
|
||||
ButtonState ButtonHandler::buttonStates[4] = {};
|
||||
TaskHandle_t buttonTaskHandle = NULL;
|
||||
const TickType_t debounceDelay = pdMS_TO_TICKS(50);
|
||||
TickType_t lastDebounceTime = 0;
|
||||
|
||||
#ifdef IS_BTCLOCK_V8
|
||||
#define BTN_1 256
|
||||
|
@ -16,86 +16,45 @@ ButtonState ButtonHandler::buttonStates[4] = {};
|
|||
#define BTN_4 256
|
||||
#endif
|
||||
|
||||
void ButtonHandler::buttonTask(void *parameter) {
|
||||
void buttonTask(void *parameter) {
|
||||
while (1) {
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
TickType_t currentTime = xTaskGetTickCount();
|
||||
|
||||
std::lock_guard<std::mutex> lock(mcpMutex);
|
||||
|
||||
if (!digitalRead(MCP_INT_PIN)) {
|
||||
uint16_t intFlags = mcp1.getInterruptFlagRegister();
|
||||
uint16_t intCap = mcp1.getInterruptCaptureRegister();
|
||||
|
||||
// Check button states
|
||||
if (intFlags & BTN_1) handleButtonPress(0);
|
||||
if (intFlags & BTN_2) handleButtonPress(1);
|
||||
if (intFlags & BTN_3) handleButtonPress(2);
|
||||
if (intFlags & BTN_4) handleButtonPress(3);
|
||||
|
||||
// Check for button releases
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (buttonStates[i].isPressed) {
|
||||
bool currentlyPressed = false;
|
||||
switch (i) {
|
||||
case 0: currentlyPressed = (intCap & BTN_1); break;
|
||||
case 1: currentlyPressed = (intCap & BTN_2); break;
|
||||
case 2: currentlyPressed = (intCap & BTN_3); break;
|
||||
case 3: currentlyPressed = (intCap & BTN_4); break;
|
||||
}
|
||||
if (!currentlyPressed) {
|
||||
handleButtonRelease(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear interrupt state
|
||||
while (!digitalRead(MCP_INT_PIN)) {
|
||||
mcp1.getInterruptCaptureRegister();
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonHandler::handleButtonPress(int buttonIndex) {
|
||||
TickType_t currentTime = xTaskGetTickCount();
|
||||
ButtonState &state = buttonStates[buttonIndex];
|
||||
|
||||
if ((currentTime - state.lastPressTime) >= debounceDelay) {
|
||||
state.isPressed = true;
|
||||
state.lastPressTime = currentTime;
|
||||
}
|
||||
}
|
||||
if ((currentTime - lastDebounceTime) >= debounceDelay) {
|
||||
lastDebounceTime = currentTime;
|
||||
|
||||
void ButtonHandler::handleButtonRelease(int buttonIndex) {
|
||||
ButtonState &state = buttonStates[buttonIndex];
|
||||
if (!digitalRead(MCP_INT_PIN)) {
|
||||
uint pin = mcp1.getInterruptFlagRegister();
|
||||
|
||||
if (!state.isPressed) return; // Ignore if button wasn't pressed
|
||||
|
||||
state.isPressed = false;
|
||||
handleSingleClick(buttonIndex);
|
||||
}
|
||||
|
||||
void ButtonHandler::handleSingleClick(int buttonIndex) {
|
||||
switch (buttonIndex) {
|
||||
case 0:
|
||||
switch (pin) {
|
||||
case BTN_1:
|
||||
toggleTimerActive();
|
||||
break;
|
||||
case 1:
|
||||
ScreenHandler::nextScreen();
|
||||
case BTN_2:
|
||||
nextScreen();
|
||||
break;
|
||||
case 2:
|
||||
ScreenHandler::previousScreen();
|
||||
case BTN_3:
|
||||
previousScreen();
|
||||
break;
|
||||
case 3:
|
||||
ScreenHandler::showSystemStatusScreen();
|
||||
case BTN_4:
|
||||
showSystemStatusScreen();
|
||||
break;
|
||||
}
|
||||
}
|
||||
mcp1.getInterruptCaptureRegister();
|
||||
} else {
|
||||
}
|
||||
// Very ugly, but for some reason this is necessary
|
||||
while (!digitalRead(MCP_INT_PIN)) {
|
||||
mcp1.getInterruptCaptureRegister();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR ButtonHandler::handleButtonInterrupt() {
|
||||
void IRAM_ATTR handleButtonInterrupt() {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
xTaskNotifyFromISR(buttonTaskHandle, 0, eNoAction, &xHigherPriorityTaskWoken);
|
||||
if (xHigherPriorityTaskWoken == pdTRUE) {
|
||||
|
@ -103,8 +62,9 @@ void IRAM_ATTR ButtonHandler::handleButtonInterrupt() {
|
|||
}
|
||||
}
|
||||
|
||||
void ButtonHandler::setup() {
|
||||
void setupButtonTask() {
|
||||
xTaskCreate(buttonTask, "ButtonTask", 3072, NULL, tskIDLE_PRIORITY,
|
||||
&buttonTaskHandle);
|
||||
attachInterrupt(MCP_INT_PIN, handleButtonInterrupt, FALLING);
|
||||
&buttonTaskHandle); // Create the FreeRTOS task
|
||||
// Use interrupt instead of task
|
||||
attachInterrupt(MCP_INT_PIN, handleButtonInterrupt, CHANGE);
|
||||
}
|
||||
|
|
|
@ -6,49 +6,8 @@
|
|||
#include "lib/shared.hpp"
|
||||
#include "lib/timers.hpp"
|
||||
|
||||
// Track timing for each button
|
||||
struct ButtonState {
|
||||
TickType_t lastPressTime = 0;
|
||||
TickType_t pressStartTime = 0;
|
||||
bool isPressed = false;
|
||||
uint8_t clickCount = 0;
|
||||
bool longPressHandled = false;
|
||||
};
|
||||
extern TaskHandle_t buttonTaskHandle;
|
||||
|
||||
class ButtonHandler {
|
||||
private:
|
||||
static const TickType_t debounceDelay = pdMS_TO_TICKS(50);
|
||||
static const TickType_t doubleClickDelay = pdMS_TO_TICKS(1000); // Maximum time between clicks for double click
|
||||
static const TickType_t longPressDelay = pdMS_TO_TICKS(1500); // Time to hold for long press
|
||||
|
||||
static ButtonState buttonStates[4];
|
||||
static TaskHandle_t buttonTaskHandle;
|
||||
|
||||
// Button handlers
|
||||
static void handleButtonPress(int buttonIndex);
|
||||
static void handleButtonRelease(int buttonIndex);
|
||||
static void handleSingleClick(int buttonIndex);
|
||||
static void handleDoubleClick(int buttonIndex);
|
||||
static void handleLongPress(int buttonIndex);
|
||||
|
||||
// Task function
|
||||
static void buttonTask(void *pvParameters);
|
||||
|
||||
public:
|
||||
static void setup();
|
||||
static void IRAM_ATTR handleButtonInterrupt();
|
||||
static void suspendTask() { if (buttonTaskHandle != NULL) vTaskSuspend(buttonTaskHandle); }
|
||||
static void resumeTask() { if (buttonTaskHandle != NULL) vTaskResume(buttonTaskHandle); }
|
||||
|
||||
#ifdef IS_BTCLOCK_V8
|
||||
static const uint16_t BTN_1 = 256;
|
||||
static const uint16_t BTN_2 = 512;
|
||||
static const uint16_t BTN_3 = 1024;
|
||||
static const uint16_t BTN_4 = 2048;
|
||||
#else
|
||||
static const uint16_t BTN_1 = 2048;
|
||||
static const uint16_t BTN_2 = 1024;
|
||||
static const uint16_t BTN_3 = 512;
|
||||
static const uint16_t BTN_4 = 256;
|
||||
#endif
|
||||
};
|
||||
void buttonTask(void *pvParameters);
|
||||
void IRAM_ATTR handleButtonInterrupt();
|
||||
void setupButtonTask();
|
||||
|
|
|
@ -25,23 +25,6 @@ void addScreenMapping(int value, const char *name)
|
|||
screenMappings.push_back({value, name});
|
||||
}
|
||||
|
||||
void setupDataSource()
|
||||
{
|
||||
DataSourceType dataSource = getDataSource();
|
||||
bool zapNotifyEnabled = preferences.getBool("nostrZapNotify", DEFAULT_ZAP_NOTIFY_ENABLED);
|
||||
|
||||
// Setup Nostr if it's either the data source or zap notifications are enabled
|
||||
if (dataSource == NOSTR_SOURCE || zapNotifyEnabled) {
|
||||
setupNostrNotify(dataSource == NOSTR_SOURCE, zapNotifyEnabled);
|
||||
setupNostrTask();
|
||||
}
|
||||
// Setup other data sources if Nostr is not the data source
|
||||
if (dataSource != NOSTR_SOURCE) {
|
||||
xTaskCreate(setupWebsocketClients, "setupWebsocketClients", 8192, NULL,
|
||||
tskIDLE_PRIORITY, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
setupPreferences();
|
||||
|
@ -95,20 +78,24 @@ void setup()
|
|||
setupTasks();
|
||||
setupTimers();
|
||||
|
||||
// Setup data sources (includes Nostr zap notifications if enabled)
|
||||
setupDataSource();
|
||||
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);
|
||||
}
|
||||
|
||||
if (preferences.getBool("bitaxeEnabled", DEFAULT_BITAXE_ENABLED))
|
||||
{
|
||||
setupBitaxeFetchTask();
|
||||
}
|
||||
|
||||
if (preferences.getBool("miningPoolStats", DEFAULT_MINING_POOL_STATS_ENABLED))
|
||||
{
|
||||
setupMiningPoolStatsFetchTask();
|
||||
}
|
||||
|
||||
ButtonHandler::setup();
|
||||
setupButtonTask();
|
||||
setupOTA();
|
||||
|
||||
waitUntilNoneBusy();
|
||||
|
@ -292,30 +279,10 @@ void setupPreferences()
|
|||
setBlockHeight(preferences.getUInt("blockHeight", INITIAL_BLOCK_HEIGHT));
|
||||
setPrice(preferences.getUInt("lastPrice", INITIAL_LAST_PRICE), CURRENCY_USD);
|
||||
|
||||
if (!preferences.isKey("enableDebugLog")) {
|
||||
preferences.putBool("enableDebugLog", DEFAULT_ENABLE_DEBUG_LOG);
|
||||
}
|
||||
|
||||
if (!preferences.isKey("dataSource")) {
|
||||
preferences.putUChar("dataSource", DEFAULT_DATA_SOURCE);
|
||||
}
|
||||
|
||||
// Initialize custom endpoint settings if not set
|
||||
if (!preferences.isKey("customEndpoint")) {
|
||||
preferences.putString("customEndpoint", DEFAULT_CUSTOM_ENDPOINT);
|
||||
}
|
||||
|
||||
if (!preferences.isKey("customEndpointDisableSSL")) {
|
||||
preferences.putBool("customEndpointDisableSSL", DEFAULT_CUSTOM_ENDPOINT_DISABLE_SSL);
|
||||
}
|
||||
|
||||
// Set currency based on data source
|
||||
DataSourceType dataSource = static_cast<DataSourceType>(preferences.getUChar("dataSource", DEFAULT_DATA_SOURCE));
|
||||
if (dataSource == BTCLOCK_SOURCE || dataSource == CUSTOM_SOURCE) {
|
||||
ScreenHandler::setCurrentCurrency(preferences.getUChar("lastCurrency", CURRENCY_USD));
|
||||
} else {
|
||||
ScreenHandler::setCurrentCurrency(CURRENCY_USD);
|
||||
}
|
||||
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE))
|
||||
setCurrentCurrency(preferences.getUChar("lastCurrency", CURRENCY_USD));
|
||||
else
|
||||
setCurrentCurrency(CURRENCY_USD);
|
||||
|
||||
if (!preferences.isKey("flDisable")) {
|
||||
preferences.putBool("flDisable", isWhiteVersion() ? false : true);
|
||||
|
@ -364,14 +331,6 @@ void setupPreferences()
|
|||
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)
|
||||
|
@ -387,15 +346,75 @@ String replaceAmbiguousChars(String input)
|
|||
return input;
|
||||
}
|
||||
|
||||
// void addCurrencyMappings(const std::vector<std::string>& currencies)
|
||||
// {
|
||||
// for (const auto& currency : currencies)
|
||||
// {
|
||||
// int satsPerCurrencyScreen;
|
||||
// int btcTickerScreen;
|
||||
// int marketCapScreen;
|
||||
|
||||
// // Determine the corresponding screen IDs based on the currency code
|
||||
// if (currency == "USD")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_USD;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_USD;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_USD;
|
||||
// }
|
||||
// else if (currency == "EUR")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_EUR;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_EUR;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_EUR;
|
||||
// }
|
||||
// else if (currency == "GBP")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_GBP;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_GBP;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_GBP;
|
||||
// }
|
||||
// else if (currency == "JPY")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_JPY;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_JPY;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_JPY;
|
||||
// }
|
||||
// else if (currency == "AUD")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_AUD;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_AUD;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_AUD;
|
||||
// }
|
||||
// else if (currency == "CAD")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_CAD;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_CAD;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_CAD;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// continue; // Unknown currency, skip it
|
||||
// }
|
||||
|
||||
// // Create the string locally to ensure it persists
|
||||
// std::string satsPerCurrencyString = "Sats per " + currency;
|
||||
// std::string btcTickerString = "Ticker " + currency;
|
||||
// std::string marketCapString = "Market Cap " + currency;
|
||||
|
||||
// // Pass the c_str() to the function
|
||||
// addScreenMapping(satsPerCurrencyScreen, satsPerCurrencyString.c_str());
|
||||
// addScreenMapping(btcTickerScreen, btcTickerString.c_str());
|
||||
// addScreenMapping(marketCapScreen, marketCapString.c_str());
|
||||
// }
|
||||
// }
|
||||
|
||||
void setupWebsocketClients(void *pvParameters)
|
||||
{
|
||||
DataSourceType dataSource = getDataSource();
|
||||
|
||||
if (dataSource == BTCLOCK_SOURCE || dataSource == CUSTOM_SOURCE)
|
||||
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE))
|
||||
{
|
||||
V2Notify::setupV2Notify();
|
||||
}
|
||||
else if (dataSource == THIRD_PARTY_SOURCE)
|
||||
else
|
||||
{
|
||||
setupBlockNotify();
|
||||
setupPriceNotify();
|
||||
|
@ -469,19 +488,15 @@ void setupHardware()
|
|||
Serial.println(F("Error loading WebUI"));
|
||||
}
|
||||
|
||||
// if (!LittleFS.exists("/qr.txt"))
|
||||
// {
|
||||
// File f = LittleFS.open("/qr.txt", "w");
|
||||
|
||||
// if(f) {
|
||||
// if (f.print("Hello")) {
|
||||
// Serial.println(F("Written QR to FS"));
|
||||
// Serial.printf("\nLittleFS free: %zu\n", LittleFS.totalBytes() - LittleFS.usedBytes());
|
||||
// }
|
||||
|
||||
// } else {
|
||||
// Serial.println(F("Can't write QR to FS"));
|
||||
// }
|
||||
|
||||
// f.close();
|
||||
// }
|
||||
|
||||
setupLeds();
|
||||
|
@ -496,34 +511,32 @@ void setupHardware()
|
|||
|
||||
Wire.begin(I2C_SDA_PIN, I2C_SCK_PIN, 400000);
|
||||
|
||||
if (!mcp1.begin()) {
|
||||
if (!mcp1.begin())
|
||||
{
|
||||
Serial.println(F("Error MCP23017 1"));
|
||||
} else {
|
||||
|
||||
// while (1)
|
||||
// ;
|
||||
}
|
||||
else
|
||||
{
|
||||
pinMode(MCP_INT_PIN, INPUT_PULLUP);
|
||||
// mcp1.setupInterrupts(false, false, LOW);
|
||||
mcp1.enableControlRegister(MCP23x17_IOCR_ODR);
|
||||
|
||||
// Enable mirrored interrupts (both INTA and INTB pins signal any interrupt)
|
||||
if (!mcp1.mirrorInterrupts(true)) {
|
||||
Serial.println(F("Error setting up mirrored interrupts"));
|
||||
}
|
||||
mcp1.mirrorInterrupts(true);
|
||||
|
||||
// Configure all 4 button pins as inputs with pullups and interrupts
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!mcp1.pinMode1(i, INPUT_PULLUP)) {
|
||||
Serial.printf("Error setting pin %d to input pull up\n", i);
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
mcp1.pinMode1(i, INPUT_PULLUP);
|
||||
mcp1.enableInterrupt(i, LOW);
|
||||
}
|
||||
// Enable interrupt on CHANGE for each pin
|
||||
if (!mcp1.enableInterrupt(i, CHANGE)) {
|
||||
Serial.printf("Error enabling interrupt for pin %d\n", i);
|
||||
#ifndef IS_BTCLOCK_V8
|
||||
for (int i = 8; i <= 14; i++)
|
||||
{
|
||||
mcp1.pinMode1(i, OUTPUT);
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef IS_HW_REV_B
|
||||
|
@ -798,15 +811,18 @@ const char* getWebUiFilename() {
|
|||
}
|
||||
}
|
||||
|
||||
bool debugLogEnabled()
|
||||
{
|
||||
return preferences.getBool("enableDebugLog", DEFAULT_ENABLE_DEBUG_LOG);
|
||||
}
|
||||
|
||||
DataSourceType getDataSource() {
|
||||
return static_cast<DataSourceType>(preferences.getUChar("dataSource", DEFAULT_DATA_SOURCE));
|
||||
}
|
||||
// void loadIcons() {
|
||||
// size_t ocean_logo_size = 886;
|
||||
|
||||
void setDataSource(DataSourceType source) {
|
||||
preferences.putUChar("dataSource", static_cast<uint8_t>(source));
|
||||
}
|
||||
// int iUncompSize = zt.gzip_info((uint8_t *)epd_compress_bitaxe, ocean_logo_size);
|
||||
// Serial.printf("uncompressed size = %d\n", iUncompSize);
|
||||
|
||||
// uint8_t *pUncompressed;
|
||||
// pUncompressed = (uint8_t *)malloc(iUncompSize+4);
|
||||
// int rc = zt.gunzip((uint8_t *)epd_compress_bitaxe, ocean_logo_size, pUncompressed);
|
||||
|
||||
// if (rc == ZT_SUCCESS) {
|
||||
// Serial.println("Decode success");
|
||||
// }
|
||||
// }
|
|
@ -18,7 +18,6 @@
|
|||
#include "lib/ota.hpp"
|
||||
#include "lib/nostr_notify.hpp"
|
||||
#include "lib/bitaxe_fetch.hpp"
|
||||
#include "lib/mining_pool_stats_fetch.hpp"
|
||||
|
||||
#include "lib/v2_notify.hpp"
|
||||
|
||||
|
@ -31,11 +30,9 @@
|
|||
#include "BH1750.h"
|
||||
#endif
|
||||
|
||||
#include "shared.hpp"
|
||||
#include "defaults.hpp"
|
||||
|
||||
#define NTP_SERVER "pool.ntp.org"
|
||||
#define DEFAULT_TIME_OFFSET_SECONDS 3600
|
||||
#define USER_AGENT "BTClock/3.0"
|
||||
#ifndef MCP_DEV_ADDR
|
||||
#define MCP_DEV_ADDR 0x20
|
||||
#endif
|
||||
|
@ -80,8 +77,6 @@ String getHwRev();
|
|||
bool isWhiteVersion();
|
||||
String getFsRev();
|
||||
|
||||
bool debugLogEnabled();
|
||||
|
||||
void addScreenMapping(int value, const char* name);
|
||||
// void addScreenMapping(int value, const String& name);
|
||||
// void addScreenMapping(int value, const std::string& name);
|
||||
|
@ -91,13 +86,3 @@ String replaceAmbiguousChars(String input);
|
|||
const char* getFirmwareFilename();
|
||||
const char* getWebUiFilename();
|
||||
// void loadIcons();
|
||||
|
||||
extern Preferences preferences;
|
||||
extern MCP23017 mcp1;
|
||||
#ifdef IS_BTCLOCK_V8
|
||||
extern MCP23017 mcp2;
|
||||
#endif
|
||||
|
||||
// Expose DataSourceType enum
|
||||
extern DataSourceType getDataSource();
|
||||
extern void setDataSource(DataSourceType source);
|
|
@ -1,6 +1,4 @@
|
|||
#pragma once
|
||||
|
||||
#define INITIAL_BLOCK_HEIGHT 876600
|
||||
#define INITIAL_BLOCK_HEIGHT 851500
|
||||
#define INITIAL_LAST_PRICE 50000
|
||||
#define DEFAULT_TX_POWER 0
|
||||
|
||||
|
@ -18,11 +16,14 @@
|
|||
#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"
|
||||
|
@ -57,15 +58,10 @@
|
|||
#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_FONT_NAME "antonio"
|
||||
|
||||
#define DEFAULT_HTTP_AUTH_ENABLED false
|
||||
#define DEFAULT_HTTP_AUTH_USERNAME "btclock"
|
||||
|
@ -75,26 +71,3 @@
|
|||
|
||||
#define DEFAULT_GIT_RELEASE_URL "https://git.btclock.dev/api/v1/repos/btclock/btclock_v3/releases/latest"
|
||||
#define DEFAULT_VERTICAL_DESC true
|
||||
|
||||
#define DEFAULT_MINING_POOL_LOGOS_URL "https://git.btclock.dev/btclock/mining-pool-logos/raw/branch/main"
|
||||
|
||||
#define DEFAULT_ENABLE_DEBUG_LOG false
|
||||
|
||||
#define DEFAULT_DISABLE_FL false
|
||||
#define DEFAULT_CUSTOM_ENDPOINT "ws-staging.btclock.dev"
|
||||
#define DEFAULT_CUSTOM_ENDPOINT_DISABLE_SSL false
|
||||
#define DEFAULT_MOW_MODE false
|
||||
|
||||
// Define data source types
|
||||
enum DataSourceType {
|
||||
BTCLOCK_SOURCE = 0, // BTClock's own data source
|
||||
THIRD_PARTY_SOURCE = 1, // Third party data sources like mempool.space
|
||||
NOSTR_SOURCE = 2, // Nostr data source
|
||||
CUSTOM_SOURCE = 3 // Custom data source endpoint
|
||||
};
|
||||
|
||||
#define DEFAULT_DATA_SOURCE BTCLOCK_SOURCE
|
||||
|
||||
#ifndef DEFAULT_BOOT_TEXT
|
||||
#define DEFAULT_BOOT_TEXT "BTCLOCK"
|
||||
#endif
|
298
src/lib/epd.cpp
298
src/lib/epd.cpp
|
@ -123,20 +123,10 @@ QueueHandle_t updateQueue;
|
|||
int fgColor = GxEPD_WHITE;
|
||||
int bgColor = GxEPD_BLACK;
|
||||
|
||||
struct FontFamily {
|
||||
GFXfont* big;
|
||||
GFXfont* medium;
|
||||
GFXfont* small;
|
||||
};
|
||||
|
||||
FontFamily antonioFonts = {nullptr, nullptr, nullptr};
|
||||
FontFamily oswaldFonts = {nullptr, nullptr, nullptr};
|
||||
|
||||
const GFXfont *FONT_SMALL;
|
||||
const GFXfont *FONT_BIG;
|
||||
const GFXfont *FONT_MEDIUM;
|
||||
const GFXfont *FONT_SATSYMBOL;
|
||||
|
||||
#define FONT_SMALL Antonio_SemiBold20pt7b
|
||||
#define FONT_BIG Antonio_SemiBold90pt7b
|
||||
#define FONT_MEDIUM Antonio_SemiBold40pt7b
|
||||
#define FONT_SATSYMBOL Satoshi_Symbol90pt7b
|
||||
std::mutex epdUpdateMutex;
|
||||
std::mutex epdMutex[NUM_SCREENS];
|
||||
|
||||
|
@ -148,9 +138,6 @@ uint8_t qrcode[800];
|
|||
#define EPD_TASK_STACK_SIZE 2048
|
||||
#endif
|
||||
|
||||
#define BUSY_TIMEOUT_COUNT 200
|
||||
#define BUSY_RETRY_DELAY pdMS_TO_TICKS(10)
|
||||
|
||||
void forceFullRefresh()
|
||||
{
|
||||
for (uint i = 0; i < NUM_SCREENS; i++)
|
||||
|
@ -159,70 +146,65 @@ void forceFullRefresh()
|
|||
}
|
||||
}
|
||||
|
||||
GFXfont font90;
|
||||
void refreshFromMemory()
|
||||
{
|
||||
for (uint i = 0; i < NUM_SCREENS; i++)
|
||||
{
|
||||
int *taskParam = new int;
|
||||
*taskParam = i;
|
||||
|
||||
void loadFonts(const String& fontName) {
|
||||
if (fontName == FontNames::ANTONIO) {
|
||||
// Load Antonio fonts
|
||||
antonioFonts.big = FontLoader::loadCompressedFont(Antonio_SemiBold90pt7b_Properties);
|
||||
antonioFonts.medium = FontLoader::loadCompressedFont(Antonio_SemiBold40pt7b_Properties);
|
||||
antonioFonts.small = FontLoader::loadCompressedFont(Antonio_SemiBold20pt7b_Properties);
|
||||
|
||||
FONT_BIG = antonioFonts.big;
|
||||
FONT_MEDIUM = antonioFonts.medium;
|
||||
FONT_SMALL = antonioFonts.small;
|
||||
} else if (fontName == FontNames::OSWALD) {
|
||||
// Load Oswald fonts
|
||||
oswaldFonts.big = FontLoader::loadCompressedFont(Oswald_Medium80pt7b_Properties);
|
||||
oswaldFonts.medium = FontLoader::loadCompressedFont(Oswald_Medium30pt7b_Properties);
|
||||
oswaldFonts.small = FontLoader::loadCompressedFont(Oswald_Medium20pt7b_Properties);
|
||||
|
||||
FONT_BIG = oswaldFonts.big;
|
||||
FONT_MEDIUM = oswaldFonts.medium;
|
||||
FONT_SMALL = oswaldFonts.small;
|
||||
xTaskCreate(
|
||||
[](void *pvParameters)
|
||||
{
|
||||
const int epdIndex = *(int *)pvParameters;
|
||||
delete (int *)pvParameters;
|
||||
displays[epdIndex].refresh(false);
|
||||
vTaskDelete(NULL);
|
||||
},
|
||||
"PrepareUpd", 4096, taskParam, tskIDLE_PRIORITY, NULL);
|
||||
}
|
||||
|
||||
FONT_SATSYMBOL = &Satoshi_Symbol90pt7b;
|
||||
}
|
||||
|
||||
void setupDisplays() {
|
||||
// Load fonts based on preference
|
||||
String fontName = preferences.getString("fontName", DEFAULT_FONT_NAME);
|
||||
loadFonts(fontName);
|
||||
|
||||
// Initialize displays
|
||||
void setupDisplays()
|
||||
{
|
||||
std::lock_guard<std::mutex> lockMcp(mcpMutex);
|
||||
for (uint i = 0; i < NUM_SCREENS; i++) {
|
||||
|
||||
for (uint i = 0; i < NUM_SCREENS; i++)
|
||||
{
|
||||
displays[i].init(0, true, 30);
|
||||
}
|
||||
|
||||
// Create update queue and task
|
||||
updateQueue = xQueueCreate(UPDATE_QUEUE_SIZE, sizeof(UpdateDisplayTaskItem));
|
||||
xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", EPD_TASK_STACK_SIZE * 2, NULL, 11, NULL);
|
||||
|
||||
// Create display update tasks
|
||||
for (uint i = 0; i < NUM_SCREENS; i++) {
|
||||
xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", EPD_TASK_STACK_SIZE, NULL, 11, NULL);
|
||||
|
||||
for (uint i = 0; i < NUM_SCREENS; i++)
|
||||
{
|
||||
// epdUpdateSemaphore[i] = xSemaphoreCreateBinary();
|
||||
// xSemaphoreGive(epdUpdateSemaphore[i]);
|
||||
|
||||
int *taskParam = new int;
|
||||
*taskParam = i;
|
||||
xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), EPD_TASK_STACK_SIZE, taskParam, 11, &tasks[i]);
|
||||
|
||||
xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), EPD_TASK_STACK_SIZE, taskParam,
|
||||
11, &tasks[i]); // create task
|
||||
}
|
||||
|
||||
// Check for storage mode (prevents burn-in)
|
||||
if (mcp1.read1(0) == LOW) {
|
||||
// Hold lower button to enable "storage mode" (prevents burn-in of ePaper displays)
|
||||
if (mcp1.read1(0) == LOW)
|
||||
{
|
||||
setFgColor(GxEPD_BLACK);
|
||||
setBgColor(GxEPD_WHITE);
|
||||
|
||||
epdContent.fill("");
|
||||
} else {
|
||||
// Initialize with custom text or default
|
||||
String customText = preferences.getString("displayText", DEFAULT_BOOT_TEXT);
|
||||
std::array<String, NUM_SCREENS> newContent;
|
||||
newContent.fill(" ");
|
||||
|
||||
for (size_t i = 0; i < std::min(customText.length(), (size_t)NUM_SCREENS); i++) {
|
||||
newContent[i] = String(customText[i]);
|
||||
}
|
||||
|
||||
epdContent = newContent;
|
||||
else
|
||||
{
|
||||
#ifdef IS_BTCLOCK_V8
|
||||
epdContent = {"B", "T", "C", "L", "O", "C", "K", "v8"};
|
||||
#else
|
||||
epdContent = {"B", "T", "C", "L", "O", "C", "K"};
|
||||
#endif
|
||||
}
|
||||
|
||||
setEpdContent(epdContent);
|
||||
|
@ -293,11 +275,7 @@ void prepareDisplayUpdateTask(void *pvParameters)
|
|||
}
|
||||
else if (epdContent[epdIndex].startsWith(F("mdi")))
|
||||
{
|
||||
bool updated = renderIcon(epdIndex, epdContent[epdIndex], updatePartial);
|
||||
if (!updated)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
renderIcon(epdIndex, epdContent[epdIndex], updatePartial);
|
||||
}
|
||||
else if (epdContent[epdIndex].length() > 5)
|
||||
{
|
||||
|
@ -305,28 +283,27 @@ void prepareDisplayUpdateTask(void *pvParameters)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (epdContent[epdIndex].length() == 2)
|
||||
{
|
||||
showChars(epdIndex, epdContent[epdIndex], updatePartial, FONT_BIG);
|
||||
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"))
|
||||
{
|
||||
showDigit(epdIndex, 'S', updatePartial,
|
||||
FONT_SATSYMBOL);
|
||||
&FONT_SATSYMBOL);
|
||||
}
|
||||
else
|
||||
{
|
||||
showChars(epdIndex, epdContent[epdIndex], updatePartial,
|
||||
FONT_MEDIUM);
|
||||
&FONT_MEDIUM);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
showDigit(epdIndex, epdContent[epdIndex].c_str()[0], updatePartial,
|
||||
FONT_BIG);
|
||||
&FONT_BIG);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -396,15 +373,12 @@ extern "C" void updateDisplay(void *pvParameters) noexcept
|
|||
void splitText(const uint dispNum, const String &top, const String &bottom,
|
||||
bool partial)
|
||||
{
|
||||
if (preferences.getBool("verticalDesc", DEFAULT_VERTICAL_DESC) && dispNum == 0)
|
||||
{
|
||||
if(preferences.getBool("verticalDesc", DEFAULT_VERTICAL_DESC) && dispNum == 0) {
|
||||
displays[dispNum].setRotation(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
displays[dispNum].setRotation(2);
|
||||
}
|
||||
displays[dispNum].setFont(FONT_SMALL);
|
||||
displays[dispNum].setFont(&FONT_SMALL);
|
||||
displays[dispNum].setTextColor(getFgColor());
|
||||
|
||||
// Top text
|
||||
|
@ -440,51 +414,97 @@ void splitText(const uint dispNum, const String &top, const String &bottom,
|
|||
displays[dispNum].print(bottom);
|
||||
}
|
||||
|
||||
// Consolidate common display setup code into a helper function
|
||||
void setupDisplay(const uint dispNum, const GFXfont *font)
|
||||
{
|
||||
displays[dispNum].setRotation(2);
|
||||
displays[dispNum].setFont(font);
|
||||
displays[dispNum].setTextColor(getFgColor());
|
||||
displays[dispNum].fillScreen(getBgColor());
|
||||
}
|
||||
// void showChars(const uint dispNum, const String &chars, bool partial,
|
||||
// const GFXfont *font)
|
||||
// {
|
||||
// displays[dispNum].setRotation(2);
|
||||
// displays[dispNum].setFont(font);
|
||||
// displays[dispNum].setTextColor(getFgColor());
|
||||
// int16_t tbx, tby;
|
||||
// uint16_t tbw, tbh;
|
||||
|
||||
void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font)
|
||||
// displays[dispNum].getTextBounds(chars, 0, 0, &tbx, &tby, &tbw, &tbh);
|
||||
|
||||
// // center the bounding box by transposition of the origin:
|
||||
// uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
|
||||
// uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
|
||||
|
||||
// displays[dispNum].fillScreen(getBgColor());
|
||||
|
||||
// displays[dispNum].setCursor(x, y);
|
||||
// displays[dispNum].print(chars);
|
||||
|
||||
// // displays[dispNum].setCursor(10, 3);
|
||||
// // displays[dispNum].setFont(&FONT_SMALL);
|
||||
// // displays[dispNum].setTextColor(getFgColor());
|
||||
// // displays[dispNum].println("Y = " + y);
|
||||
// }
|
||||
|
||||
void showDigit(const uint dispNum, char chr, bool partial,
|
||||
const GFXfont *font)
|
||||
{
|
||||
String str(chr);
|
||||
|
||||
if (chr == '.')
|
||||
{
|
||||
str = "!";
|
||||
}
|
||||
|
||||
setupDisplay(dispNum, font);
|
||||
|
||||
displays[dispNum].setRotation(2);
|
||||
displays[dispNum].setFont(font);
|
||||
displays[dispNum].setTextColor(getFgColor());
|
||||
int16_t tbx, tby;
|
||||
uint16_t tbw, tbh;
|
||||
|
||||
displays[dispNum].getTextBounds(str, 0, 0, &tbx, &tby, &tbw, &tbh);
|
||||
|
||||
// center the bounding box by transposition of the origin:
|
||||
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
|
||||
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
|
||||
|
||||
// if (str.equals("."))
|
||||
// {
|
||||
// // int16_t yAdvance = font->yAdvance;
|
||||
// // uint8_t charIndex = 46 - font->first;
|
||||
// // GFXglyph *glyph = (&font->glyph)[charIndex];
|
||||
// int16_t tbx2, tby2;
|
||||
// uint16_t tbw2, tbh2;
|
||||
// displays[dispNum].getTextBounds(".!", 0, 0, &tbx2, &tby2, &tbw2, &tbh2);
|
||||
|
||||
// y = ((displays[dispNum].height() - tbh2) / 2) - tby2;
|
||||
// // Serial.print("yAdvance");
|
||||
// // Serial.println(yAdvance);
|
||||
// // if (glyph != nullptr) {
|
||||
// // Serial.print("height");
|
||||
// // Serial.println(glyph->height);
|
||||
// // Serial.print("yOffset");
|
||||
// // Serial.println(glyph->yOffset);
|
||||
// // }
|
||||
|
||||
// // y = 250-99+18+19;
|
||||
// }
|
||||
|
||||
displays[dispNum].fillScreen(getBgColor());
|
||||
|
||||
displays[dispNum].setCursor(x, y);
|
||||
displays[dispNum].print(str);
|
||||
|
||||
if (chr == '.')
|
||||
{
|
||||
displays[dispNum].fillRect(0, 0, displays[dispNum].width(),
|
||||
round(displays[dispNum].height() * 0.67), getBgColor());
|
||||
displays[dispNum].fillRect(x, y, displays[dispNum].width(), round(displays[dispNum].height() * 0.9), getBgColor());
|
||||
}
|
||||
|
||||
// displays[dispNum].setCursor(10, 3);
|
||||
// displays[dispNum].setFont(&FONT_SMALL);
|
||||
// displays[dispNum].setTextColor(getFgColor());
|
||||
// displays[dispNum].println("Y = " + y);
|
||||
}
|
||||
|
||||
int16_t calculateDescent(const GFXfont *font)
|
||||
{
|
||||
int16_t calculateDescent(const GFXfont *font) {
|
||||
int16_t maxDescent = 0;
|
||||
for (uint16_t i = font->first; i <= font->last; i++)
|
||||
{
|
||||
for (uint16_t i = font->first; i <= font->last; i++) {
|
||||
GFXglyph *glyph = &font->glyph[i - font->first];
|
||||
int16_t descent = glyph->yOffset;
|
||||
if (descent > maxDescent)
|
||||
{
|
||||
if (descent > maxDescent) {
|
||||
maxDescent = descent;
|
||||
}
|
||||
}
|
||||
|
@ -494,31 +514,33 @@ int16_t calculateDescent(const GFXfont *font)
|
|||
void showChars(const uint dispNum, const String &chars, bool partial,
|
||||
const GFXfont *font)
|
||||
{
|
||||
setupDisplay(dispNum, font);
|
||||
|
||||
displays[dispNum].setRotation(2);
|
||||
displays[dispNum].setFont(font);
|
||||
displays[dispNum].setTextColor(getFgColor());
|
||||
int16_t tbx, tby;
|
||||
uint16_t tbw, tbh;
|
||||
displays[dispNum].getTextBounds(chars, 0, 0, &tbx, &tby, &tbw, &tbh);
|
||||
|
||||
int16_t descent = calculateDescent(font);
|
||||
|
||||
// center the bounding box by transposition of the origin:
|
||||
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
|
||||
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
|
||||
displays[dispNum].fillScreen(getBgColor());
|
||||
// displays[dispNum].setCursor(x, y);
|
||||
// displays[dispNum].print(chars);
|
||||
|
||||
for (int i = 0; i < chars.length(); i++)
|
||||
{
|
||||
for (int i = 0; i < chars.length(); i++) {
|
||||
char c = chars[i];
|
||||
if (c == '.' || c == ',')
|
||||
{
|
||||
if (c == '.' || c == ',') {
|
||||
// For the dot, calculate its specific descent
|
||||
GFXglyph *dotGlyph = &font->glyph[c - font->first];
|
||||
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
|
||||
{
|
||||
} else {
|
||||
// For other characters, use the original y-position
|
||||
displays[dispNum].setCursor(x, y);
|
||||
displays[dispNum].print(c);
|
||||
|
@ -572,7 +594,7 @@ void renderText(const uint dispNum, const String &text, bool partial)
|
|||
}
|
||||
}
|
||||
|
||||
bool renderIcon(const uint dispNum, const String &text, bool partial)
|
||||
void renderIcon(const uint dispNum, const String &text, bool partial)
|
||||
{
|
||||
displays[dispNum].setRotation(2);
|
||||
|
||||
|
@ -582,49 +604,29 @@ bool renderIcon(const uint dispNum, const String &text, bool partial)
|
|||
displays[dispNum].setTextColor(getFgColor());
|
||||
|
||||
uint iconIndex = 0;
|
||||
uint width = 122;
|
||||
uint height = 122;
|
||||
if (text.endsWith("rocket"))
|
||||
{
|
||||
if (text.endsWith("rocket")) {
|
||||
iconIndex = 1;
|
||||
}
|
||||
else if (text.endsWith("lnbolt"))
|
||||
{
|
||||
iconIndex = 2;
|
||||
}
|
||||
else if (text.endsWith("bitaxe"))
|
||||
{
|
||||
width = 88;
|
||||
height = 220;
|
||||
else if (text.endsWith("lnbolt")) {
|
||||
iconIndex = 3;
|
||||
}
|
||||
else if (text.endsWith("miningpool"))
|
||||
{
|
||||
LogoData logo = getMiningPoolLogo();
|
||||
|
||||
if (logo.size == 0)
|
||||
{
|
||||
Serial.println(F("No logo found"));
|
||||
return false;
|
||||
else if (text.endsWith("bitaxe")) {
|
||||
iconIndex = 4;
|
||||
}
|
||||
|
||||
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());
|
||||
displays[dispNum].drawInvertedBitmap(0,0, epd_icons_allArray[iconIndex], 122, 250, getFgColor());
|
||||
|
||||
|
||||
// displays[dispNum].drawInvertedBitmap(0,0, getOceanIcon(), 122, 250, getFgColor());
|
||||
|
||||
|
||||
return true;
|
||||
// displays[dispNum].drawInvertedBitmap(0,0, getOceanIcon(), 122, 250, getFgColor());
|
||||
}
|
||||
|
||||
|
||||
|
||||
void renderQr(const uint dispNum, const String &text, bool partial)
|
||||
{
|
||||
#ifdef USE_QR
|
||||
|
@ -668,15 +670,15 @@ void waitUntilNoneBusy()
|
|||
while (EPD_BUSY[i].digitalRead())
|
||||
{
|
||||
count++;
|
||||
vTaskDelay(BUSY_RETRY_DELAY);
|
||||
|
||||
if (count == BUSY_TIMEOUT_COUNT)
|
||||
vTaskDelay(10);
|
||||
if (count == 200)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
// displays[i].init(0, false);
|
||||
vTaskDelay(100);
|
||||
}
|
||||
else if (count > BUSY_TIMEOUT_COUNT + 5)
|
||||
else if (count > 205)
|
||||
{
|
||||
log_e("Display %d busy timeout", i);
|
||||
Serial.printf("Busy timeout %d", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include <Fonts/FreeSans9pt7b.h>
|
||||
#include <Fonts/FreeSansBold9pt7b.h>
|
||||
#include <GxEPD2_BW.h>
|
||||
#include "gzip_decompressor.hpp"
|
||||
|
||||
|
||||
#include <mcp23x17_pin.hpp>
|
||||
#include <mutex>
|
||||
|
@ -14,31 +14,19 @@
|
|||
#include "lib/config.hpp"
|
||||
#include "lib/shared.hpp"
|
||||
#include "icons/icons.h"
|
||||
#include "mining_pool_stats_fetch.hpp"
|
||||
|
||||
// Font includes
|
||||
#include "../fonts/antonio-semibold20.h"
|
||||
#include "../fonts/antonio-semibold40.h"
|
||||
#include "../fonts/antonio-semibold90.h"
|
||||
|
||||
// Oswald fonts
|
||||
#include "../fonts/oswald-medium20.h"
|
||||
#include "../fonts/oswald-medium30.h"
|
||||
#include "../fonts/oswald-medium80.h"
|
||||
|
||||
#include "../fonts/sats-symbol.h"
|
||||
|
||||
#ifdef USE_QR
|
||||
#include "qrcodegen.h"
|
||||
#endif
|
||||
// extern TaskHandle_t epdTaskHandle;
|
||||
|
||||
typedef struct {
|
||||
char dispNum;
|
||||
} UpdateDisplayTaskItem;
|
||||
|
||||
void forceFullRefresh();
|
||||
void refreshFromMemory();
|
||||
void setupDisplays();
|
||||
void loadFonts(const String& fontName);
|
||||
|
||||
void splitText(const uint dispNum, const String &top, const String &bottom,
|
||||
bool partial);
|
||||
|
@ -56,7 +44,7 @@ int getFgColor();
|
|||
void setBgColor(int color);
|
||||
void setFgColor(int color);
|
||||
|
||||
bool renderIcon(const uint dispNum, const String &text, bool partial);
|
||||
void renderIcon(const uint dispNum, const String &text, bool partial);
|
||||
void renderText(const uint dispNum, const String &text, bool partial);
|
||||
void renderQr(const uint dispNum, const String &text, bool partial);
|
||||
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "rom/miniz.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
class GzipDecompressor {
|
||||
public:
|
||||
static bool decompressData(const uint8_t* input, size_t inputSize,
|
||||
uint8_t* output, size_t* outputSize) {
|
||||
if (!input || !output || !outputSize || inputSize < 18) { // Minimum gzip size
|
||||
return false;
|
||||
}
|
||||
|
||||
tinfl_decompressor* decomp = new tinfl_decompressor;
|
||||
if (!decomp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tinfl_init(decomp);
|
||||
|
||||
size_t inPos = 10; // Skip gzip header
|
||||
size_t outPos = 0;
|
||||
|
||||
while (inPos < inputSize - 8) { // -8 for footer
|
||||
size_t inBytes = inputSize - inPos - 8;
|
||||
size_t outBytes = *outputSize - outPos;
|
||||
|
||||
tinfl_status status = tinfl_decompress(decomp,
|
||||
&input[inPos], &inBytes,
|
||||
output, &output[outPos], &outBytes,
|
||||
TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
|
||||
|
||||
inPos += inBytes;
|
||||
outPos += outBytes;
|
||||
|
||||
if (status == TINFL_STATUS_DONE) {
|
||||
*outputSize = outPos;
|
||||
delete decomp;
|
||||
return true;
|
||||
} else if (status < 0) {
|
||||
delete decomp;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
delete decomp;
|
||||
return false;
|
||||
}
|
||||
};
|
|
@ -6,7 +6,7 @@ Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
|
|||
uint ledTaskParams;
|
||||
|
||||
#ifdef HAS_FRONTLIGHT
|
||||
constexpr uint16_t FL_FADE_STEP = 25;
|
||||
#define FL_FADE_STEP 25
|
||||
|
||||
bool frontlightOn = false;
|
||||
bool flInTransition = false;
|
||||
|
@ -50,9 +50,6 @@ void frontlightFadeOut(uint num)
|
|||
|
||||
void frontlightSetBrightness(uint brightness)
|
||||
{
|
||||
if (isDNDActive()) {
|
||||
return; // Don't change brightness during DND mode
|
||||
}
|
||||
if (brightness > 4096)
|
||||
{
|
||||
return;
|
||||
|
@ -71,15 +68,18 @@ void frontlightFadeInAll(int flDelayTime)
|
|||
|
||||
void frontlightFadeInAll(int flDelayTime, bool staggered)
|
||||
{
|
||||
if (preferences.getBool("flDisable") || frontlightIsOn() || flInTransition)
|
||||
if (preferences.getBool("flDisable"))
|
||||
return;
|
||||
if (frontlightIsOn())
|
||||
return;
|
||||
if (flInTransition)
|
||||
return;
|
||||
|
||||
flInTransition = true;
|
||||
|
||||
const int maxBrightness = preferences.getUInt("flMaxBrightness");
|
||||
|
||||
if (staggered)
|
||||
{
|
||||
int maxBrightness = preferences.getUInt("flMaxBrightness");
|
||||
int step = FL_FADE_STEP;
|
||||
int staggerDelay = flDelayTime / NUM_SCREENS;
|
||||
|
||||
|
@ -100,7 +100,7 @@ void frontlightFadeInAll(int flDelayTime, bool staggered)
|
|||
}
|
||||
else
|
||||
{
|
||||
for (int dutyCycle = 0; dutyCycle <= maxBrightness; dutyCycle += FL_FADE_STEP)
|
||||
for (int dutyCycle = 0; dutyCycle <= preferences.getUInt("flMaxBrightness"); dutyCycle += FL_FADE_STEP)
|
||||
{
|
||||
for (int ledPin = 0; ledPin <= NUM_SCREENS; ledPin++)
|
||||
{
|
||||
|
@ -186,9 +186,6 @@ bool frontlightIsOn()
|
|||
|
||||
void frontlightFadeIn(uint num, int flDelayTime)
|
||||
{
|
||||
if (isDNDActive()) {
|
||||
return; // Don't change brightness during DND mode
|
||||
}
|
||||
if (preferences.getBool("flDisable"))
|
||||
return;
|
||||
for (int dutyCycle = 0; dutyCycle <= preferences.getUInt("flMaxBrightness"); dutyCycle += 5)
|
||||
|
@ -200,9 +197,6 @@ void frontlightFadeIn(uint num, int flDelayTime)
|
|||
|
||||
void frontlightFadeOut(uint num, int flDelayTime)
|
||||
{
|
||||
if (isDNDActive()) {
|
||||
return; // Don't change brightness during DND mode
|
||||
}
|
||||
if (preferences.getBool("flDisable"))
|
||||
return;
|
||||
if (!frontlightIsOn())
|
||||
|
@ -216,85 +210,6 @@ void frontlightFadeOut(uint num, int flDelayTime)
|
|||
}
|
||||
#endif
|
||||
|
||||
// Do Not Disturb mode variables
|
||||
bool dndEnabled = false;
|
||||
bool dndTimeBasedEnabled = false;
|
||||
DNDTimeRange dndTimeRange = {23, 0, 7, 0}; // Default: 23:00 to 07:00
|
||||
|
||||
void loadDNDSettings() {
|
||||
dndEnabled = preferences.getBool("dndEnabled", false);
|
||||
dndTimeBasedEnabled = preferences.getBool("dndTimeEnabled", false);
|
||||
|
||||
dndTimeRange.startHour = preferences.getUChar("dndStartHour", 23);
|
||||
dndTimeRange.startMinute = preferences.getUChar("dndStartMin", 0);
|
||||
dndTimeRange.endHour = preferences.getUChar("dndEndHour", 7);
|
||||
dndTimeRange.endMinute = preferences.getUChar("dndEndMin", 0);
|
||||
}
|
||||
|
||||
void setDNDEnabled(bool enabled) {
|
||||
dndEnabled = enabled;
|
||||
preferences.putBool("dndEnabled", enabled);
|
||||
if (enabled && isDNDActive()) {
|
||||
clearLeds();
|
||||
#ifdef HAS_FRONTLIGHT
|
||||
frontlightFadeOutAll();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void setDNDTimeBasedEnabled(bool enabled) {
|
||||
dndTimeBasedEnabled = enabled;
|
||||
preferences.putBool("dndTimeEnabled", enabled);
|
||||
if (enabled && isDNDActive()) {
|
||||
clearLeds();
|
||||
#ifdef HAS_FRONTLIGHT
|
||||
frontlightFadeOutAll();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void setDNDTimeRange(uint8_t startHour, uint8_t startMinute, uint8_t endHour, uint8_t endMinute) {
|
||||
dndTimeRange.startHour = startHour;
|
||||
dndTimeRange.startMinute = startMinute;
|
||||
dndTimeRange.endHour = endHour;
|
||||
dndTimeRange.endMinute = endMinute;
|
||||
|
||||
preferences.putUChar("dndStartHour", startHour);
|
||||
preferences.putUChar("dndStartMin", startMinute);
|
||||
preferences.putUChar("dndEndHour", endHour);
|
||||
preferences.putUChar("dndEndMin", endMinute);
|
||||
}
|
||||
|
||||
bool isTimeInDNDRange(uint8_t hour, uint8_t minute) {
|
||||
uint16_t currentTime = hour * 60 + minute;
|
||||
uint16_t startTime = dndTimeRange.startHour * 60 + dndTimeRange.startMinute;
|
||||
uint16_t endTime = dndTimeRange.endHour * 60 + dndTimeRange.endMinute;
|
||||
|
||||
if (startTime <= endTime) {
|
||||
// Simple case: start time is before end time (e.g., 09:00 to 17:00)
|
||||
return currentTime >= startTime && currentTime < endTime;
|
||||
} else {
|
||||
// Complex case: start time is after end time (e.g., 23:00 to 07:00)
|
||||
return currentTime >= startTime || currentTime < endTime;
|
||||
}
|
||||
}
|
||||
|
||||
bool isDNDActive() {
|
||||
if (dndEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dndTimeBasedEnabled) {
|
||||
time_t now;
|
||||
struct tm timeinfo;
|
||||
time(&now);
|
||||
localtime_r(&now, &timeinfo);
|
||||
return isTimeInDNDRange(timeinfo.tm_hour, timeinfo.tm_min);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ledTask(void *parameter)
|
||||
{
|
||||
while (1)
|
||||
|
@ -310,7 +225,7 @@ void ledTask(void *parameter)
|
|||
continue;
|
||||
}
|
||||
|
||||
std::array<uint32_t, NEOPIXEL_COUNT> oldLights;
|
||||
uint32_t oldLights[NEOPIXEL_COUNT];
|
||||
|
||||
// get current state
|
||||
for (int i = 0; i < NEOPIXEL_COUNT; i++)
|
||||
|
@ -538,7 +453,6 @@ void ledTask(void *parameter)
|
|||
|
||||
void setupLeds()
|
||||
{
|
||||
loadDNDSettings();
|
||||
pixels.begin();
|
||||
pixels.setBrightness(preferences.getUInt("ledBrightness", DEFAULT_LED_BRIGHTNESS));
|
||||
pixels.clear();
|
||||
|
@ -604,7 +518,7 @@ void blinkDelayColor(int d, int times, uint r, uint g, uint b)
|
|||
pixels.show();
|
||||
}
|
||||
|
||||
void blinkDelayTwoColor(int d, int times, const uint32_t& c1, const uint32_t& c2)
|
||||
void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2)
|
||||
{
|
||||
for (int j = 0; j < times; j++)
|
||||
{
|
||||
|
@ -684,10 +598,8 @@ void restoreLedState()
|
|||
|
||||
QueueHandle_t getLedTaskQueue() { return ledTaskQueue; }
|
||||
|
||||
bool queueLedEffect(uint effect) {
|
||||
if (isDNDActive()) {
|
||||
return false; // Don't queue any effects during DND mode
|
||||
}
|
||||
bool queueLedEffect(uint effect)
|
||||
{
|
||||
if (ledTaskQueue == NULL)
|
||||
{
|
||||
return false;
|
||||
|
@ -695,7 +607,6 @@ bool queueLedEffect(uint effect) {
|
|||
|
||||
uint flashType = effect;
|
||||
xQueueSend(ledTaskQueue, &flashType, portMAX_DELAY);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ledRainbow(int wait)
|
||||
|
|
|
@ -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_ERASE_SETTINGS = 104;
|
||||
|
||||
|
||||
const int LED_PROGRESS_25 = 200;
|
||||
const int LED_PROGRESS_50 = 201;
|
||||
const int LED_PROGRESS_75 = 202;
|
||||
|
@ -48,7 +49,7 @@ void setupLeds();
|
|||
void setupLedTask();
|
||||
void blinkDelay(int d, int times);
|
||||
void blinkDelayColor(int d, int times, uint r, uint g, uint b);
|
||||
void blinkDelayTwoColor(int d, int times, const uint32_t& c1, const uint32_t& c2);
|
||||
void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2);
|
||||
void clearLeds();
|
||||
void saveLedState();
|
||||
void restoreLedState();
|
||||
|
@ -82,21 +83,3 @@ void frontlightFadeOutAll(int flDelayTime, bool staggered);
|
|||
void frontlightFadeIn(uint num, int flDelayTime);
|
||||
void frontlightFadeOut(uint num, int flDelayTime);
|
||||
#endif
|
||||
|
||||
// Do Not Disturb mode settings
|
||||
struct DNDTimeRange {
|
||||
uint8_t startHour;
|
||||
uint8_t startMinute;
|
||||
uint8_t endHour;
|
||||
uint8_t endMinute;
|
||||
};
|
||||
|
||||
extern bool dndEnabled;
|
||||
extern bool dndTimeBasedEnabled;
|
||||
extern DNDTimeRange dndTimeRange;
|
||||
|
||||
void setDNDEnabled(bool enabled);
|
||||
void setDNDTimeBasedEnabled(bool enabled);
|
||||
void setDNDTimeRange(uint8_t startHour, uint8_t startMinute, uint8_t endHour, uint8_t endMinute);
|
||||
bool isDNDActive();
|
||||
bool isTimeInDNDRange(uint8_t hour, uint8_t minute);
|
|
@ -1,43 +0,0 @@
|
|||
#include "brains_pool.hpp"
|
||||
|
||||
void BraiinsPool::prepareRequest(HTTPClient &http) const
|
||||
{
|
||||
http.addHeader("Pool-Auth-Token", poolUser.c_str());
|
||||
}
|
||||
|
||||
std::string BraiinsPool::getApiUrl() const
|
||||
{
|
||||
return "https://pool.braiins.com/accounts/profile/json/btc/";
|
||||
}
|
||||
|
||||
PoolStats BraiinsPool::parseResponse(const JsonDocument &doc) const
|
||||
{
|
||||
try
|
||||
{
|
||||
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)};
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Serial.printf("Error parsing %s response: %s\n", getPoolName().c_str(), e.what());
|
||||
return PoolStats{
|
||||
.hashrate = "0",
|
||||
.dailyEarnings = std::nullopt};
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#include <icons/icons.h>
|
||||
#include <utils.hpp>
|
||||
|
||||
class BraiinsPool : public MiningPoolInterface
|
||||
{
|
||||
public:
|
||||
void setPoolUser(const std::string &user) override { poolUser = user; }
|
||||
void prepareRequest(HTTPClient &http) const override;
|
||||
std::string getApiUrl() const override;
|
||||
PoolStats parseResponse(const JsonDocument &doc) const override;
|
||||
bool supportsDailyEarnings() const override { return true; }
|
||||
bool hasLogo() const override { return true; }
|
||||
std::string getDisplayLabel() const override { return "BRAIINS/POOL"; } // Fallback if needed
|
||||
std::string getDailyEarningsLabel() const override { return "sats/earned"; }
|
||||
std::string getLogoFilename() const override {
|
||||
return "braiins.bin";
|
||||
}
|
||||
|
||||
std::string getPoolName() const override {
|
||||
return "braiins";
|
||||
}
|
||||
|
||||
int getLogoWidth() const override {
|
||||
return 37;
|
||||
}
|
||||
|
||||
int getLogoHeight() const override {
|
||||
return 230;
|
||||
}
|
||||
};
|
|
@ -1,47 +0,0 @@
|
|||
#include "ckpool.hpp"
|
||||
|
||||
void CKPool::prepareRequest(HTTPClient &http) const
|
||||
{
|
||||
// Empty as CKPool doesn't need special headers
|
||||
}
|
||||
|
||||
std::string CKPool::getApiUrl() const
|
||||
{
|
||||
return getBaseUrl() + "/users/" + poolUser;
|
||||
}
|
||||
|
||||
PoolStats CKPool::parseResponse(const JsonDocument &doc) const
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string hashrateStr = doc["hashrate1m"].as<std::string>();
|
||||
|
||||
// Special case for "0"
|
||||
if (hashrateStr == "0") {
|
||||
return PoolStats{
|
||||
.hashrate = "0",
|
||||
.dailyEarnings = std::nullopt
|
||||
};
|
||||
}
|
||||
|
||||
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};
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Serial.printf("Error parsing %s response: %s\n", getPoolName().c_str(), e.what());
|
||||
return PoolStats{
|
||||
.hashrate = "0",
|
||||
.dailyEarnings = std::nullopt};
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#include <utils.hpp>
|
||||
|
||||
class CKPool : 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 false; }
|
||||
std::string getDisplayLabel() const override { return "CK/POOL"; }
|
||||
std::string getPoolName() const override {
|
||||
return "ckpool";
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual std::string getBaseUrl() const {
|
||||
return "https://solo.ckpool.org";
|
||||
}
|
||||
};
|
|
@ -1,16 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "ckpool.hpp"
|
||||
|
||||
class EUCKPool : public CKPool {
|
||||
public:
|
||||
std::string getDisplayLabel() const override { return "CK/POOL"; }
|
||||
std::string getPoolName() const override {
|
||||
return "eu_ckpool";
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string getBaseUrl() const override {
|
||||
return "https://eusolo.ckpool.org";
|
||||
}
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
// src/noderunners/noderunners_pool.cpp
|
||||
#include "gobrrr_pool.hpp"
|
||||
|
||||
std::string GoBrrrPool::getApiUrl() const {
|
||||
return "https://pool.gobrrr.me/api/client/" + poolUser;
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#include "lib/mining_pool/public_pool/public_pool.hpp"
|
||||
|
||||
#include <icons/icons.h>
|
||||
|
||||
class GoBrrrPool : public PublicPool {
|
||||
public:
|
||||
std::string getApiUrl() const override;
|
||||
bool hasLogo() const override { return true; }
|
||||
std::string getDisplayLabel() const override { return "GOBRRR/POOL"; }
|
||||
|
||||
std::string getLogoFilename() const override {
|
||||
return "gobrrr.bin";
|
||||
}
|
||||
|
||||
std::string getPoolName() const override {
|
||||
return "gobrrr_pool";
|
||||
}
|
||||
|
||||
int getLogoWidth() const override {
|
||||
return 122;
|
||||
}
|
||||
|
||||
int getLogoHeight() const override {
|
||||
return 122;
|
||||
}
|
||||
};
|
|
@ -1,11 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <stddef.h>
|
||||
|
||||
struct LogoData {
|
||||
const uint8_t* data;
|
||||
size_t width;
|
||||
size_t height;
|
||||
size_t size;
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
#include "mining_pool_interface.hpp"
|
||||
#include "pool_factory.hpp"
|
||||
|
||||
LogoData MiningPoolInterface::getLogo() const {
|
||||
if (!hasLogo()) {
|
||||
return LogoData{nullptr, 0, 0, 0};
|
||||
}
|
||||
|
||||
// Check if logo exists
|
||||
String logoPath = String(PoolFactory::getLogosDir()) + "/" + String(getPoolName().c_str()) + "_logo.bin";
|
||||
|
||||
if (!LittleFS.exists(logoPath)) {
|
||||
return LogoData{nullptr, 0, 0, 0};
|
||||
}
|
||||
|
||||
// Now load the logo (whether it was just downloaded or already existed)
|
||||
return PoolFactory::loadLogoFromFS(getPoolName(), this);
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <HTTPClient.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include "pool_stats.hpp"
|
||||
#include "logo_data.hpp"
|
||||
#include "lib/shared.hpp"
|
||||
|
||||
class MiningPoolInterface {
|
||||
public:
|
||||
virtual ~MiningPoolInterface() = default;
|
||||
virtual void setPoolUser(const std::string& user) = 0;
|
||||
virtual void prepareRequest(HTTPClient& http) const = 0;
|
||||
virtual std::string getApiUrl() const = 0;
|
||||
virtual PoolStats parseResponse(const JsonDocument& doc) const = 0;
|
||||
virtual bool hasLogo() const = 0;
|
||||
virtual LogoData getLogo() const;
|
||||
virtual std::string getDisplayLabel() const = 0;
|
||||
virtual bool supportsDailyEarnings() const = 0;
|
||||
virtual std::string getDailyEarningsLabel() const = 0;
|
||||
virtual std::string getLogoFilename() const { return ""; }
|
||||
virtual std::string getPoolName() const = 0;
|
||||
virtual int getLogoWidth() const { return 0; }
|
||||
virtual int getLogoHeight() const { return 0; }
|
||||
std::string getLogoUrl() const {
|
||||
if (!hasLogo() || getLogoFilename().empty()) {
|
||||
return "";
|
||||
}
|
||||
std::string baseUrl = preferences.getString("poolLogosUrl", DEFAULT_MINING_POOL_LOGOS_URL).c_str();
|
||||
return baseUrl + "/" + getLogoFilename().c_str();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string poolUser;
|
||||
};
|
|
@ -1,95 +0,0 @@
|
|||
#include "mining_pool_stats_handler.hpp"
|
||||
|
||||
std::array<std::string, NUM_SCREENS> parseMiningPoolStatsHashRate(const std::string& hashrate, const MiningPoolInterface& pool)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> ret;
|
||||
ret.fill(""); // Initialize all elements to empty strings
|
||||
std::string label;
|
||||
std::string output;
|
||||
|
||||
parseHashrateString(hashrate, label, output, 4);
|
||||
|
||||
std::size_t textLength = output.length();
|
||||
// Calculate the position where the digits should start
|
||||
// Account for the position of the mining pool logo and the hashrate label
|
||||
std::size_t startIndex = NUM_SCREENS - 1 - textLength;
|
||||
|
||||
// Insert the pickaxe icon just before the digits
|
||||
if (startIndex > 0)
|
||||
{
|
||||
ret[startIndex - 1] = "mdi:pickaxe";
|
||||
}
|
||||
|
||||
// Place the digits
|
||||
for (std::size_t i = 0; i < textLength; ++i)
|
||||
{
|
||||
ret[startIndex + i] = output.substr(i, 1);
|
||||
}
|
||||
|
||||
ret[NUM_SCREENS - 1] = label;
|
||||
|
||||
if (pool.hasLogo()) {
|
||||
ret[0] = "mdi:miningpool";
|
||||
} else {
|
||||
ret[0] = pool.getDisplayLabel();
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
std::array<std::string, NUM_SCREENS> parseMiningPoolStatsDailyEarnings(int sats, std::string label, const MiningPoolInterface& pool)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> ret;
|
||||
ret.fill(""); // Initialize all elements to empty strings
|
||||
std::string satsDisplay = std::to_string(sats);
|
||||
|
||||
if (sats >= 100000000) {
|
||||
// A whale mining 1+ BTC per day! No decimal points; whales scoff at such things.
|
||||
label = "BTC" + label.substr(4);
|
||||
satsDisplay = satsDisplay.substr(0, satsDisplay.length() - 8);
|
||||
} else if (sats >= 10000000) {
|
||||
// 10.0M to 99.9M you get one decimal point
|
||||
satsDisplay = satsDisplay.substr(0, satsDisplay.length() - 6) + "." + satsDisplay[2] + "M";
|
||||
} else if (sats >= 1000000) {
|
||||
// 1.00M to 9.99M you get two decimal points
|
||||
satsDisplay = satsDisplay.substr(0, satsDisplay.length() - 6) + "." + satsDisplay.substr(2, 2) + "M";
|
||||
} else if (sats >= 100000) {
|
||||
// 100K to 999K you get no extra precision
|
||||
satsDisplay = satsDisplay.substr(0, satsDisplay.length() - 3) + "K";
|
||||
} else if (sats >= 10000) {
|
||||
// 10.0K to 99.9K you get one decimal point
|
||||
satsDisplay = satsDisplay.substr(0, satsDisplay.length() - 3) + "." + satsDisplay[2] + "K";
|
||||
} else {
|
||||
// Pleb miner! 4 digit or fewer sats will fit as-is. no-op.
|
||||
}
|
||||
|
||||
std::size_t textLength = satsDisplay.length();
|
||||
|
||||
// Calculate the position where the digits should start
|
||||
// Account for the position of the mining pool logo
|
||||
std::size_t startIndex = NUM_SCREENS - 1 - textLength;
|
||||
|
||||
// Insert the pickaxe icon just before the digits if there's room
|
||||
if (startIndex > 0)
|
||||
{
|
||||
ret[startIndex - 1] = "mdi:pickaxe";
|
||||
}
|
||||
|
||||
// Place the digits
|
||||
for (std::size_t i = 0; i < textLength; ++i)
|
||||
{
|
||||
ret[startIndex + i] = satsDisplay.substr(i, 1);
|
||||
}
|
||||
|
||||
ret[NUM_SCREENS - 1] = label;
|
||||
|
||||
if (pool.hasLogo()) {
|
||||
ret[0] = "mdi:miningpool";
|
||||
} else {
|
||||
ret[0] = pool.getDisplayLabel();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#include <array>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <utils.hpp>
|
||||
|
||||
#ifndef UNITY_TEST
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#endif
|
||||
|
||||
std::array<std::string, NUM_SCREENS> parseMiningPoolStatsHashRate(const std::string& hashrate, const MiningPoolInterface& pool);
|
||||
std::array<std::string, NUM_SCREENS> parseMiningPoolStatsDailyEarnings(int sats, std::string label, const MiningPoolInterface& pool);
|
|
@ -1,48 +0,0 @@
|
|||
// src/noderunners/noderunners_pool.cpp
|
||||
#include "noderunners_pool.hpp"
|
||||
|
||||
void NoderunnersPool::prepareRequest(HTTPClient &http) const
|
||||
{
|
||||
// Empty as Noderunners doesn't need special headers
|
||||
}
|
||||
|
||||
std::string NoderunnersPool::getApiUrl() const
|
||||
{
|
||||
return "https://pool.noderunners.network/api/v1/users/" + poolUser;
|
||||
}
|
||||
|
||||
PoolStats NoderunnersPool::parseResponse(const JsonDocument &doc) const
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string hashrateStr = doc["hashrate1m"].as<std::string>();
|
||||
|
||||
// Special case for "0"
|
||||
if (hashrateStr == "0") {
|
||||
return PoolStats{
|
||||
.hashrate = "0",
|
||||
.dailyEarnings = std::nullopt
|
||||
};
|
||||
}
|
||||
|
||||
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};
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Serial.printf("Error parsing %s response: %s\n", getPoolName().c_str(), e.what());
|
||||
return PoolStats{
|
||||
.hashrate = "0",
|
||||
.dailyEarnings = std::nullopt};
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#include <icons/icons.h>
|
||||
#include <utils.hpp>
|
||||
|
||||
class NoderunnersPool : public MiningPoolInterface {
|
||||
public:
|
||||
void setPoolUser(const std::string& user) override { poolUser = user; }
|
||||
|
||||
void prepareRequest(HTTPClient& http) const override;
|
||||
std::string getApiUrl() const override;
|
||||
PoolStats parseResponse(const JsonDocument& doc) const override;
|
||||
bool supportsDailyEarnings() const override { return false; }
|
||||
std::string getDailyEarningsLabel() const override { return ""; }
|
||||
bool hasLogo() const override { return true; }
|
||||
std::string getDisplayLabel() const override { return "NODE/RUNNERS"; } // Fallback if needed
|
||||
std::string getLogoFilename() const override {
|
||||
return "noderunners.bin";
|
||||
}
|
||||
|
||||
std::string getPoolName() const override {
|
||||
return "noderunners";
|
||||
}
|
||||
|
||||
int getLogoWidth() const override {
|
||||
return 122;
|
||||
}
|
||||
|
||||
int getLogoHeight() const override {
|
||||
return 122;
|
||||
}
|
||||
};
|
|
@ -1,29 +0,0 @@
|
|||
#include "ocean_pool.hpp"
|
||||
|
||||
void OceanPool::prepareRequest(HTTPClient &http) const
|
||||
{
|
||||
// Empty as Ocean doesn't need special headers
|
||||
}
|
||||
|
||||
std::string OceanPool::getApiUrl() const
|
||||
{
|
||||
return "https://api.ocean.xyz/v1/statsnap/" + poolUser;
|
||||
}
|
||||
|
||||
PoolStats OceanPool::parseResponse(const JsonDocument &doc) const
|
||||
{
|
||||
try
|
||||
{
|
||||
return PoolStats{
|
||||
.hashrate = doc["result"]["hashrate_300s"].as<std::string>(),
|
||||
.dailyEarnings = static_cast<int64_t>(
|
||||
doc["result"]["estimated_earn_next_block"].as<float>() * 100000000)};
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Serial.printf("Error parsing %s response: %s\n", getPoolName().c_str(), e.what());
|
||||
return PoolStats{
|
||||
.hashrate = "0",
|
||||
.dailyEarnings = std::nullopt};
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#include <icons/icons.h>
|
||||
|
||||
class OceanPool : public MiningPoolInterface {
|
||||
public:
|
||||
void setPoolUser(const std::string& user) override { poolUser = user; }
|
||||
void prepareRequest(HTTPClient& http) const override;
|
||||
std::string getApiUrl() const override;
|
||||
PoolStats parseResponse(const JsonDocument& doc) const override;
|
||||
bool hasLogo() const override { return true; }
|
||||
std::string getDisplayLabel() const override { return "OCEAN/POOL"; } // Fallback if needed
|
||||
bool supportsDailyEarnings() const override { return true; }
|
||||
std::string getDailyEarningsLabel() const override { return "sats/block"; }
|
||||
std::string getLogoFilename() const override {
|
||||
return "ocean.bin";
|
||||
}
|
||||
|
||||
std::string getPoolName() const override {
|
||||
return "ocean";
|
||||
}
|
||||
|
||||
int getLogoWidth() const override {
|
||||
return 122;
|
||||
}
|
||||
|
||||
int getLogoHeight() const override {
|
||||
return 122;
|
||||
}
|
||||
};
|
|
@ -1,138 +0,0 @@
|
|||
#include "pool_factory.hpp"
|
||||
|
||||
const char* PoolFactory::MINING_POOL_NAME_OCEAN = "ocean";
|
||||
const char* PoolFactory::MINING_POOL_NAME_NODERUNNERS = "noderunners";
|
||||
const char* PoolFactory::MINING_POOL_NAME_BRAIINS = "braiins";
|
||||
const char* PoolFactory::MINING_POOL_NAME_SATOSHI_RADIO = "satoshi_radio";
|
||||
const char* PoolFactory::MINING_POOL_NAME_PUBLIC_POOL = "public_pool";
|
||||
const char* PoolFactory::MINING_POOL_NAME_GOBRRR_POOL = "gobrrr_pool";
|
||||
const char* PoolFactory::MINING_POOL_NAME_CKPOOL = "ckpool";
|
||||
const char* PoolFactory::MINING_POOL_NAME_EU_CKPOOL = "eu_ckpool";
|
||||
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>(); }},
|
||||
{MINING_POOL_NAME_CKPOOL, []() { return std::make_unique<CKPool>(); }},
|
||||
{MINING_POOL_NAME_EU_CKPOOL, []() { return std::make_unique<EUCKPool>(); }}
|
||||
};
|
||||
|
||||
auto it = poolFactories.find(poolName);
|
||||
if (it == poolFactories.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second();
|
||||
}
|
||||
|
||||
void PoolFactory::downloadPoolLogo(const std::string& poolName, const MiningPoolInterface* poolInterface)
|
||||
{
|
||||
const int MAX_RETRIES = 5;
|
||||
const int RETRY_DELAY_MS = 1000; // 1 second between retries
|
||||
|
||||
if (!poolInterface || !poolInterface->hasLogo()) {
|
||||
Serial.println(F("No pool interface or logo"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure logos directory exists
|
||||
if (!LittleFS.exists(LOGOS_DIR)) {
|
||||
LittleFS.mkdir(LOGOS_DIR);
|
||||
}
|
||||
|
||||
String logoPath = String(LOGOS_DIR) + "/" + String(poolName.c_str()) + "_logo.bin";
|
||||
|
||||
// Only download if the logo doesn't exist
|
||||
if (!LittleFS.exists(logoPath)) {
|
||||
// Clean up logos directory first
|
||||
File root = LittleFS.open(LOGOS_DIR, "r");
|
||||
if (root) {
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
String path = file.path();
|
||||
file.close();
|
||||
LittleFS.remove(path);
|
||||
file = root.openNextFile();
|
||||
}
|
||||
root.close();
|
||||
}
|
||||
|
||||
// Download new logo with retries
|
||||
std::string logoUrl = poolInterface->getLogoUrl();
|
||||
if (!logoUrl.empty()) {
|
||||
for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||
Serial.printf("Downloading pool logo (attempt %d of %d)...\n", attempt, MAX_RETRIES);
|
||||
|
||||
HTTPClient http;
|
||||
http.setUserAgent(USER_AGENT);
|
||||
http.begin(logoUrl.c_str());
|
||||
int httpCode = http.GET();
|
||||
|
||||
if (httpCode == 200) {
|
||||
File file = LittleFS.open(logoPath, "w");
|
||||
if (file) {
|
||||
http.writeToStream(&file);
|
||||
file.close();
|
||||
Serial.println(F("Logo downloaded successfully"));
|
||||
http.end();
|
||||
return; // Success!
|
||||
}
|
||||
}
|
||||
|
||||
http.end();
|
||||
|
||||
if (attempt < MAX_RETRIES) {
|
||||
Serial.printf("Failed to download logo, HTTP code: %d. Retrying...\n", httpCode);
|
||||
vTaskDelay(pdMS_TO_TICKS(RETRY_DELAY_MS));
|
||||
} else {
|
||||
Serial.printf("Failed to download logo after %d attempts\n", MAX_RETRIES);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Serial.println(F("Logo already exists"));
|
||||
}
|
||||
}
|
||||
|
||||
LogoData PoolFactory::loadLogoFromFS(const std::string& poolName, const MiningPoolInterface* poolInterface)
|
||||
{
|
||||
// Initialize with dimensions from the pool interface
|
||||
LogoData logo = {nullptr,
|
||||
0,
|
||||
0,
|
||||
0};
|
||||
|
||||
String logoPath = String(LOGOS_DIR) + "/" + String(poolName.c_str()) + "_logo.bin";
|
||||
if (!LittleFS.exists(logoPath)) {
|
||||
return logo;
|
||||
}
|
||||
|
||||
// Only set dimensions if file exists
|
||||
logo.width = static_cast<size_t>(poolInterface->getLogoWidth());
|
||||
logo.height = static_cast<size_t>(poolInterface->getLogoHeight());
|
||||
|
||||
File file = LittleFS.open(logoPath, "r");
|
||||
if (!file) {
|
||||
return logo;
|
||||
}
|
||||
|
||||
size_t size = file.size();
|
||||
uint8_t* buffer = new uint8_t[size];
|
||||
|
||||
|
||||
if (file.read(buffer, size) == size) {
|
||||
logo.data = buffer;
|
||||
logo.size = size;
|
||||
} else {
|
||||
delete[] buffer;
|
||||
logo.data = nullptr;
|
||||
logo.size = 0;
|
||||
}
|
||||
|
||||
file.close();
|
||||
return logo;
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
#pragma once
|
||||
#include "mining_pool_interface.hpp"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "lib/shared.hpp"
|
||||
#include "lib/config.hpp"
|
||||
|
||||
#include "noderunners/noderunners_pool.hpp"
|
||||
#include "braiins/brains_pool.hpp"
|
||||
#include "ocean/ocean_pool.hpp"
|
||||
#include "satoshi_radio/satoshi_radio_pool.hpp"
|
||||
#include "public_pool/public_pool.hpp"
|
||||
#include "gobrrr_pool/gobrrr_pool.hpp"
|
||||
#include "ckpool/ckpool.hpp"
|
||||
#include "ckpool/eu_ckpool.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,
|
||||
MINING_POOL_NAME_CKPOOL,
|
||||
MINING_POOL_NAME_EU_CKPOOL
|
||||
};
|
||||
}
|
||||
|
||||
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* MINING_POOL_NAME_CKPOOL;
|
||||
static const char* MINING_POOL_NAME_EU_CKPOOL;
|
||||
static const char* LOGOS_DIR;
|
||||
};
|
|
@ -1,10 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
struct PoolStats {
|
||||
std::string hashrate;
|
||||
std::optional<int64_t> dailyEarnings;
|
||||
};
|
|
@ -1,32 +0,0 @@
|
|||
// src/noderunners/noderunners_pool.cpp
|
||||
#include "public_pool.hpp"
|
||||
|
||||
std::string PublicPool::getApiUrl() const
|
||||
{
|
||||
return "https://public-pool.io:40557/api/client/" + poolUser;
|
||||
}
|
||||
|
||||
PoolStats PublicPool::parseResponse(const JsonDocument &doc) const
|
||||
{
|
||||
uint64_t totalHashrate = 0;
|
||||
|
||||
try
|
||||
{
|
||||
for (JsonVariantConst worker : doc["workers"].as<JsonArrayConst>())
|
||||
{
|
||||
totalHashrate += static_cast<uint64_t>(std::llround(worker["hashRate"].as<double>()));
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Serial.printf("Error parsing %s response: %s\n", getPoolName().c_str(), e.what());
|
||||
return PoolStats{
|
||||
.hashrate = "0",
|
||||
.dailyEarnings = std::nullopt};
|
||||
}
|
||||
|
||||
return PoolStats{
|
||||
.hashrate = std::to_string(totalHashrate),
|
||||
.dailyEarnings = std::nullopt // Public Pool doesn't support daily earnings
|
||||
};
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#include "lib/mining_pool/noderunners/noderunners_pool.hpp"
|
||||
|
||||
#include <icons/icons.h>
|
||||
|
||||
class PublicPool : public NoderunnersPool {
|
||||
public:
|
||||
std::string getApiUrl() const override;
|
||||
bool hasLogo() const override { return false; }
|
||||
std::string getDisplayLabel() const override { return "PUBLIC/POOL"; }
|
||||
PoolStats parseResponse(const JsonDocument& doc) const override;
|
||||
};
|
|
@ -1,7 +0,0 @@
|
|||
// src/noderunners/noderunners_pool.cpp
|
||||
#include "satoshi_radio_pool.hpp"
|
||||
|
||||
std::string SatoshiRadioPool::getApiUrl() const
|
||||
{
|
||||
return "https://pool.satoshiradio.nl/api/v1/users/" + poolUser;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "lib/mining_pool/mining_pool_interface.hpp"
|
||||
#include "lib/mining_pool/noderunners/noderunners_pool.hpp"
|
||||
|
||||
#include <icons/icons.h>
|
||||
|
||||
class SatoshiRadioPool : public NoderunnersPool {
|
||||
public:
|
||||
std::string getApiUrl() const override;
|
||||
bool hasLogo() const override { return false; }
|
||||
std::string getDisplayLabel() const override { return "SATOSHI/RADIO"; } // Fallback if needed
|
||||
};
|
|
@ -1,137 +0,0 @@
|
|||
#include "mining_pool_stats_fetch.hpp"
|
||||
|
||||
TaskHandle_t miningPoolStatsFetchTaskHandle;
|
||||
|
||||
std::string miningPoolName;
|
||||
std::string miningPoolStatsHashrate;
|
||||
int miningPoolStatsDailyEarnings;
|
||||
|
||||
std::string getMiningPoolStatsHashRate()
|
||||
{
|
||||
return miningPoolStatsHashrate;
|
||||
}
|
||||
|
||||
int getMiningPoolStatsDailyEarnings()
|
||||
{
|
||||
return miningPoolStatsDailyEarnings;
|
||||
}
|
||||
|
||||
void taskMiningPoolStatsFetch(void *pvParameters)
|
||||
{
|
||||
std::string poolName = preferences.getString("miningPoolName", DEFAULT_MINING_POOL_NAME).c_str();
|
||||
auto poolInterface = PoolFactory::createPool(poolName);
|
||||
|
||||
std::string poolUser = preferences.getString("miningPoolUser", DEFAULT_MINING_POOL_USER).c_str();
|
||||
|
||||
// Main stats fetching loop
|
||||
for (;;)
|
||||
{
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
HTTPClient http;
|
||||
http.setUserAgent(USER_AGENT);
|
||||
|
||||
|
||||
|
||||
poolInterface->setPoolUser(poolUser);
|
||||
std::string apiUrl = poolInterface->getApiUrl();
|
||||
http.begin(apiUrl.c_str());
|
||||
if (debugLogEnabled())
|
||||
{
|
||||
Serial.printf("Fetching mining pool stats from %s\r\n", apiUrl.c_str());
|
||||
}
|
||||
poolInterface->prepareRequest(http);
|
||||
int httpCode = http.GET();
|
||||
if (httpCode == 200)
|
||||
{
|
||||
String payload = http.getString();
|
||||
JsonDocument doc;
|
||||
deserializeJson(doc, payload);
|
||||
|
||||
if (debugLogEnabled())
|
||||
{
|
||||
Serial.printf("Mining pool stats response: %s\r\n", payload.c_str());
|
||||
}
|
||||
|
||||
PoolStats stats = poolInterface->parseResponse(doc);
|
||||
|
||||
miningPoolStatsHashrate = stats.hashrate;
|
||||
|
||||
if (debugLogEnabled())
|
||||
{
|
||||
Serial.printf("Mining pool stats parsed hashrate: %s\r\n", stats.hashrate.c_str());
|
||||
}
|
||||
|
||||
if (stats.dailyEarnings)
|
||||
{
|
||||
miningPoolStatsDailyEarnings = *stats.dailyEarnings;
|
||||
}
|
||||
else
|
||||
{
|
||||
miningPoolStatsDailyEarnings = 0; // or any other default value
|
||||
}
|
||||
|
||||
if (workQueue != nullptr && (ScreenHandler::getCurrentScreen() == SCREEN_MINING_POOL_STATS_HASHRATE || ScreenHandler::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 (ScreenHandler::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;
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <HTTPClient.h>
|
||||
#include "mining_pool/pool_factory.hpp"
|
||||
|
||||
#include "lib/config.hpp"
|
||||
#include "lib/shared.hpp"
|
||||
|
||||
extern TaskHandle_t miningPoolStatsFetchTaskHandle;
|
||||
|
||||
void setupMiningPoolStatsFetchTask();
|
||||
void taskMiningPoolStatsFetch(void *pvParameters);
|
||||
|
||||
std::string getMiningPoolStatsHashRate();
|
||||
int getMiningPoolStatsDailyEarnings();
|
||||
|
||||
std::unique_ptr<MiningPoolInterface>& getMiningPool();
|
||||
LogoData getMiningPoolLogo();
|
|
@ -27,7 +27,7 @@ void setupNostrNotify(bool asDatasource, bool zapNotify)
|
|||
String pubKey = preferences.getString("nostrPubKey");
|
||||
pools.push_back(pool);
|
||||
|
||||
std::vector<nostr::NostrRelay *> *relays = pool->getConnectedRelays();
|
||||
std::vector<std::map<NostrString, std::initializer_list<NostrString>>> filters;
|
||||
|
||||
if (zapNotify)
|
||||
{
|
||||
|
@ -46,27 +46,31 @@ void setupNostrNotify(bool asDatasource, bool zapNotify)
|
|||
}},
|
||||
handleNostrEventCallback,
|
||||
onNostrSubscriptionClosed,
|
||||
onNostrSubscriptionEose
|
||||
);
|
||||
onNostrSubscriptionEose);
|
||||
|
||||
Serial.println(F("[ Nostr ] Subscribing to Nostr Data Feed"));
|
||||
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)
|
||||
relay->getConnection()->addConnectionStatusListener([&](const nostr::ConnectionStatus &status)
|
||||
{
|
||||
static const char* STATUS_STRINGS[] = {"UNKNOWN", "CONNECTED", "DISCONNECTED", "ERROR"};
|
||||
int statusIndex = static_cast<int>(status);
|
||||
|
||||
nostrIsConnected = (status == nostr::ConnectionStatus::CONNECTED);
|
||||
if (!nostrIsConnected) {
|
||||
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)
|
||||
{
|
||||
|
@ -76,8 +80,7 @@ void setupNostrNotify(bool asDatasource, bool zapNotify)
|
|||
|
||||
void nostrTask(void *pvParameters)
|
||||
{
|
||||
DataSourceType dataSource = getDataSource();
|
||||
if(dataSource == NOSTR_SOURCE) {
|
||||
if(preferences.getBool("useNostr", DEFAULT_USE_NOSTR)) {
|
||||
int blockFetch = getBlockFetch();
|
||||
processNewBlock(blockFetch);
|
||||
}
|
||||
|
@ -100,7 +103,7 @@ void nostrTask(void *pvParameters)
|
|||
|
||||
void setupNostrTask()
|
||||
{
|
||||
xTaskCreate(nostrTask, "nostrTask", 8192, NULL, 10, &nostrTaskHandle);
|
||||
xTaskCreate(nostrTask, "nostrTask", 16384, NULL, 10, &nostrTaskHandle);
|
||||
}
|
||||
|
||||
boolean nostrConnected()
|
||||
|
@ -126,57 +129,55 @@ void onNostrSubscriptionEose(const String &subId)
|
|||
|
||||
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);
|
||||
|
||||
// Early return if array is invalid
|
||||
if (arr.size() < 2 || !arr[1].is<JsonObject>()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Access the second element which is the object
|
||||
JsonObject obj = arr[1].as<JsonObject>();
|
||||
JsonArray tags = obj["tags"].as<JsonArray>();
|
||||
if (!tags) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use direct value access instead of multiple comparisons
|
||||
String typeValue;
|
||||
// Flag to check if the tag was found
|
||||
bool tagFound = false;
|
||||
uint medianFee = 0;
|
||||
String typeValue;
|
||||
|
||||
for (JsonArray tag : tags) {
|
||||
if (tag.size() != 2) continue;
|
||||
|
||||
// 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];
|
||||
if (!key) continue;
|
||||
|
||||
// Use switch for better performance on string comparisons
|
||||
switch (key[0]) {
|
||||
case 't': // type
|
||||
if (strcmp(key, "type") == 0) {
|
||||
const char *value = tag[1];
|
||||
if (value) typeValue = value;
|
||||
|
||||
// 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;
|
||||
}
|
||||
break;
|
||||
case 'm': // medianFee
|
||||
if (strcmp(key, "medianFee") == 0) {
|
||||
else if (strcmp(key, "medianFee") == 0)
|
||||
{
|
||||
medianFee = tag[1].as<uint>();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Process the data
|
||||
if (!typeValue.isEmpty()) {
|
||||
if (typeValue == "priceUsd") {
|
||||
if (tagFound)
|
||||
{
|
||||
if (typeValue.equals("priceUsd"))
|
||||
{
|
||||
processNewPrice(obj["content"].as<uint>(), CURRENCY_USD);
|
||||
}
|
||||
else if (typeValue == "blockHeight") {
|
||||
else if (typeValue.equals("blockHeight"))
|
||||
{
|
||||
processNewBlock(obj["content"].as<uint>());
|
||||
}
|
||||
|
||||
if (medianFee != 0) {
|
||||
if (medianFee != 0)
|
||||
{
|
||||
processNewBlockFee(medianFee);
|
||||
}
|
||||
}
|
||||
|
@ -201,27 +202,7 @@ void subscribeZaps(nostr::NostrPool *pool, const String &relay, int minutesAgo)
|
|||
{"kinds", {"9735"}},
|
||||
{"limit", {"1"}},
|
||||
{"since", {String(getMinutesAgo(minutesAgo))}},
|
||||
{"#p", {preferences.getString("nostrZapPubkey", DEFAULT_ZAP_NOTIFY_PUBKEY) }},
|
||||
// {"#p", [&]() {
|
||||
// std::initializer_list<NostrString> pubkeys;
|
||||
// String pubkeysStr = preferences.getString("nostrZapPubkeys", "");
|
||||
// if (pubkeysStr.length() > 0) {
|
||||
// // Assuming pubkeys are comma-separated
|
||||
// char* str = strdup(pubkeysStr.c_str());
|
||||
// char* token = strtok(str, ",");
|
||||
// std::vector<NostrString> keys;
|
||||
// while (token != NULL) {
|
||||
// keys.push_back(String(token));
|
||||
// token = strtok(NULL, ",");
|
||||
// }
|
||||
// free(str);
|
||||
// return std::initializer_list<NostrString>(keys.begin(), keys.end());
|
||||
// }
|
||||
// // Return default if no pubkeys found
|
||||
// return std::initializer_list<NostrString>{
|
||||
// preferences.getString("nostrZapPubkey", DEFAULT_ZAP_NOTIFY_PUBKEY)
|
||||
// };
|
||||
// }()},
|
||||
{"#p", {preferences.getString("nostrZapPubkey", DEFAULT_ZAP_NOTIFY_PUBKEY)}},
|
||||
},
|
||||
},
|
||||
handleNostrZapCallback,
|
||||
|
@ -231,47 +212,34 @@ void subscribeZaps(nostr::NostrPool *pool, const String &relay, int 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);
|
||||
|
||||
// Early return if invalid
|
||||
if (arr.size() < 2 || !arr[1].is<JsonObject>()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Access the second element which is the object
|
||||
JsonObject obj = arr[1].as<JsonObject>();
|
||||
JsonArray tags = obj["tags"].as<JsonArray>();
|
||||
if (!tags) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t zapAmount = 0;
|
||||
String zapPubkey;
|
||||
|
||||
for (JsonArray tag : tags) {
|
||||
if (tag.size() != 2) continue;
|
||||
|
||||
// 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 (!key || !value) continue;
|
||||
|
||||
if (key[0] == 'b' && strcmp(key, "bolt11") == 0) {
|
||||
zapAmount = getAmountInSatoshis(std::string(value));
|
||||
}
|
||||
else if (key[0] == 'p' && strcmp(key, "p") == 0) {
|
||||
zapPubkey = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (zapAmount == 0) return;
|
||||
|
||||
std::array<std::string, NUM_SCREENS> textEpdContent = parseZapNotify(zapAmount, preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL));
|
||||
|
||||
if (debugLogEnabled())
|
||||
if (strcmp(key, "bolt11") == 0)
|
||||
{
|
||||
Serial.printf("Got a zap of %llu sats for %s\n", zapAmount, zapPubkey.c_str());
|
||||
}
|
||||
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())
|
||||
|
@ -280,7 +248,7 @@ void handleNostrZapCallback(const String &subId, nostr::SignedNostrEvent *event)
|
|||
timerPeriod = getTimerSeconds();
|
||||
esp_timer_stop(screenRotateTimer);
|
||||
}
|
||||
ScreenHandler::setCurrentScreen(SCREEN_CUSTOM);
|
||||
setCurrentScreen(SCREEN_CUSTOM);
|
||||
|
||||
setEpdContent(textEpdContent);
|
||||
vTaskDelay(pdMS_TO_TICKS(315 * NUM_SCREENS) + pdMS_TO_TICKS(250));
|
||||
|
@ -293,4 +261,7 @@ void handleNostrZapCallback(const String &subId, nostr::SignedNostrEvent *event)
|
|||
esp_timer_start_periodic(screenRotateTimer,
|
||||
timerPeriod * usPerSecond);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -64,10 +64,11 @@ void onOTAStart()
|
|||
// Stop or suspend all tasks
|
||||
// vTaskSuspend(priceUpdateTaskHandle);
|
||||
// vTaskSuspend(blockUpdateTaskHandle);
|
||||
vTaskSuspend(taskScreenRotateTaskHandle);
|
||||
vTaskSuspend(workerTaskHandle);
|
||||
vTaskSuspend(eventSourceTaskHandle);
|
||||
ButtonHandler::suspendTask();
|
||||
vTaskSuspend(taskScreenRotateTaskHandle);
|
||||
|
||||
// vTaskSuspend(ledTaskHandle);
|
||||
vTaskSuspend(buttonTaskHandle);
|
||||
|
||||
// stopWebServer();
|
||||
stopBlockNotify();
|
||||
|
@ -83,7 +84,6 @@ void handleOTATask(void *parameter)
|
|||
if (xQueueReceive(otaQueue, &msg, 0) == pdTRUE)
|
||||
{
|
||||
if (msg.updateType == UPDATE_ALL) {
|
||||
isOtaUpdating = true;
|
||||
queueLedEffect(LED_FLASH_UPDATE);
|
||||
int resultWebUi = downloadUpdateHandler(UPDATE_WEBUI);
|
||||
queueLedEffect(LED_FLASH_UPDATE);
|
||||
|
@ -182,7 +182,7 @@ int downloadUpdateHandler(char updateType)
|
|||
String expectedSHA256 = downloadSHA256(latestRelease.checksumUrl);
|
||||
if (expectedSHA256.isEmpty())
|
||||
{
|
||||
Serial.println(F("Failed to get SHA256 checksum. Aborting update."));
|
||||
Serial.println("Failed to get SHA256 checksum. Aborting update.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -218,7 +218,7 @@ int downloadUpdateHandler(char updateType)
|
|||
|
||||
if (bytesRead != contentLength)
|
||||
{
|
||||
Serial.println(F("Failed to read entire firmware"));
|
||||
Serial.println("Failed to read entire firmware");
|
||||
free(firmware);
|
||||
return false;
|
||||
}
|
||||
|
@ -226,14 +226,14 @@ int downloadUpdateHandler(char updateType)
|
|||
// Calculate SHA256
|
||||
String calculated_sha256 = calculateSHA256(firmware, contentLength);
|
||||
|
||||
Serial.print(F("Calculated checksum: "));
|
||||
Serial.print("Calculated checksum: ");
|
||||
Serial.println(calculated_sha256);
|
||||
Serial.print(F("Expected checksum: "));
|
||||
Serial.print("Expected checksum: ");
|
||||
Serial.println(expectedSHA256);
|
||||
|
||||
if (calculated_sha256 != expectedSHA256)
|
||||
{
|
||||
Serial.println(F("Checksum mismatch. Aborting update."));
|
||||
Serial.println("Checksum mismatch. Aborting update.");
|
||||
free(firmware);
|
||||
return false;
|
||||
}
|
||||
|
@ -259,15 +259,15 @@ int downloadUpdateHandler(char updateType)
|
|||
|
||||
if (Update.end())
|
||||
{
|
||||
Serial.println(F("OTA done!"));
|
||||
Serial.println("OTA done!");
|
||||
if (Update.isFinished())
|
||||
{
|
||||
Serial.println(F("Update successfully completed. Rebooting."));
|
||||
Serial.println("Update successfully completed. Rebooting.");
|
||||
// ESP.restart();
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println(F("Update not finished? Something went wrong!"));
|
||||
Serial.println("Update not finished? Something went wrong!");
|
||||
free(firmware);
|
||||
return 503;
|
||||
}
|
||||
|
@ -281,14 +281,14 @@ int downloadUpdateHandler(char updateType)
|
|||
}
|
||||
else
|
||||
{
|
||||
Serial.println(F("Not enough space to begin OTA"));
|
||||
Serial.println("Not enough space to begin OTA");
|
||||
free(firmware);
|
||||
return 503;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println(F("Invalid content length"));
|
||||
Serial.println("Invalid content length");
|
||||
return 503;
|
||||
}
|
||||
}
|
||||
|
@ -338,7 +338,7 @@ void updateWebUi(String latestRelease, int command)
|
|||
Serial.println(calculated_sha256);
|
||||
if ((command == U_FLASH && expectedSHA256.equals(calculated_sha256)) || command == U_SPIFFS)
|
||||
{
|
||||
Serial.println(F("Checksum verified. Proceeding with update."));
|
||||
Serial.println("Checksum verified. Proceeding with update.");
|
||||
|
||||
Update.onProgress(onOTAProgress);
|
||||
|
||||
|
@ -349,38 +349,38 @@ void updateWebUi(String latestRelease, int command)
|
|||
Update.write(buffer, contentLength);
|
||||
if (Update.end())
|
||||
{
|
||||
Serial.println(F("Update complete. Rebooting."));
|
||||
Serial.println("Update complete. Rebooting.");
|
||||
ESP.restart();
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println(F("Error in update process."));
|
||||
Serial.println("Error in update process.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println(F("Not enough space to begin OTA"));
|
||||
Serial.println("Not enough space to begin OTA");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println(F("Checksum mismatch. Aborting update."));
|
||||
Serial.println("Checksum mismatch. Aborting update.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println(F("Error downloading firmware"));
|
||||
Serial.println("Error downloading firmware");
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println(F("Not enough memory to allocate buffer"));
|
||||
Serial.println("Not enough memory to allocate buffer");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println(F("Invalid content length"));
|
||||
Serial.println("Invalid content length");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -418,7 +418,7 @@ String downloadSHA256(const String &sha256Url)
|
|||
{
|
||||
if (sha256Url.isEmpty())
|
||||
{
|
||||
Serial.println(F("Failed to get SHA256 file URL"));
|
||||
Serial.println("Failed to get SHA256 file URL");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#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";
|
||||
|
||||
// WebsocketsClient client;
|
||||
|
@ -14,12 +17,19 @@ WebSocketsClient priceNotifyWs;
|
|||
|
||||
void setupPriceNotify()
|
||||
{
|
||||
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE))
|
||||
{
|
||||
config = {.uri = wsOwnServerPrice,
|
||||
.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);
|
||||
esp_websocket_register_events(clientPrice, WEBSOCKET_EVENT_ANY,
|
||||
|
@ -76,6 +86,10 @@ void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
|
|||
break;
|
||||
case WEBSOCKET_EVENT_DATA:
|
||||
onWebsocketPriceMessage(data);
|
||||
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE))
|
||||
{
|
||||
onWebsocketBlockMessage(data);
|
||||
}
|
||||
break;
|
||||
case WEBSOCKET_EVENT_ERROR:
|
||||
Serial.println(F("Price WS Connnection error"));
|
||||
|
@ -110,42 +124,23 @@ void processNewPrice(uint newPrice, char currency)
|
|||
if (lastUpdateMap.find(currency) == lastUpdateMap.end() ||
|
||||
(currentTime - lastUpdateMap[currency]) > minSecPriceUpd)
|
||||
{
|
||||
// const unsigned long oldPrice = currentPrice;
|
||||
currencyMap[currency] = newPrice;
|
||||
|
||||
// Store price in preferences if enough time has passed
|
||||
if (lastUpdateMap[currency] == 0 || (currentTime - lastUpdateMap[currency]) > 120)
|
||||
if (currency == CURRENCY_USD && ( lastUpdateMap[currency] == 0 ||
|
||||
(currentTime - lastUpdateMap[currency]) > 120))
|
||||
{
|
||||
String prefKey = String("lastPrice_") + getCurrencyCode(currency).c_str();
|
||||
preferences.putUInt(prefKey.c_str(), newPrice);
|
||||
preferences.putUInt("lastPrice", currentPrice);
|
||||
}
|
||||
|
||||
lastUpdateMap[currency] = currentTime;
|
||||
|
||||
if (workQueue != nullptr && (ScreenHandler::getCurrentScreen() == SCREEN_BTC_TICKER ||
|
||||
ScreenHandler::getCurrentScreen() == SCREEN_SATS_PER_CURRENCY ||
|
||||
ScreenHandler::getCurrentScreen() == SCREEN_MARKET_CAP))
|
||||
// if (abs((int)(oldPrice-currentPrice)) > round(0.0015*oldPrice)) {
|
||||
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER ||
|
||||
getCurrentScreen() == SCREEN_SATS_PER_CURRENCY ||
|
||||
getCurrentScreen() == SCREEN_MARKET_CAP))
|
||||
{
|
||||
WorkItem priceUpdate = {TASK_PRICE_UPDATE, currency};
|
||||
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loadStoredPrices()
|
||||
{
|
||||
// Load prices for all supported currencies
|
||||
std::vector<std::string> currencies = getAvailableCurrencies();
|
||||
|
||||
for (const std::string ¤cy : currencies) {
|
||||
// Get first character as the currency identifier
|
||||
String prefKey = String("lastPrice_") + currency.c_str();
|
||||
uint storedPrice = preferences.getUInt(prefKey.c_str(), 0);
|
||||
|
||||
if (storedPrice > 0) {
|
||||
currencyMap[getCurrencyChar(currency)] = storedPrice;
|
||||
// Initialize lastUpdateMap to 0 so next update will store immediately
|
||||
lastUpdateMap[getCurrencyChar(currency)] = 0;
|
||||
}
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,4 +28,3 @@ void restartPriceNotify();
|
|||
|
||||
bool getPriceNotifyInit();
|
||||
uint getLastPriceUpdate(char currency);
|
||||
void loadStoredPrices();
|
|
@ -1,253 +1,51 @@
|
|||
#include "screen_handler.hpp"
|
||||
|
||||
// TaskHandle_t priceUpdateTaskHandle;
|
||||
// TaskHandle_t blockUpdateTaskHandle;
|
||||
// TaskHandle_t timeUpdateTaskHandle;
|
||||
TaskHandle_t taskScreenRotateTaskHandle;
|
||||
TaskHandle_t workerTaskHandle;
|
||||
QueueHandle_t workQueue = NULL;
|
||||
|
||||
// Initialize static members
|
||||
uint ScreenHandler::currentScreen = SCREEN_BLOCK_HEIGHT;
|
||||
uint ScreenHandler::currentCurrency = CURRENCY_USD;
|
||||
|
||||
std::array<std::string, NUM_SCREENS> taskEpdContent = {};
|
||||
std::string priceString;
|
||||
|
||||
// Convert existing functions to static member functions
|
||||
void ScreenHandler::setCurrentScreen(uint newScreen) {
|
||||
if (newScreen != SCREEN_CUSTOM) {
|
||||
preferences.putUInt("currentScreen", newScreen);
|
||||
}
|
||||
currentScreen = newScreen;
|
||||
#define WORK_QUEUE_SIZE 10
|
||||
QueueHandle_t workQueue = NULL;
|
||||
|
||||
switch (currentScreen) {
|
||||
case SCREEN_TIME: {
|
||||
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
|
||||
xQueueSend(workQueue, &timeUpdate, portMAX_DELAY);
|
||||
break;
|
||||
}
|
||||
case SCREEN_HALVING_COUNTDOWN:
|
||||
case SCREEN_BLOCK_HEIGHT: {
|
||||
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
|
||||
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
||||
break;
|
||||
}
|
||||
case SCREEN_MARKET_CAP:
|
||||
case SCREEN_SATS_PER_CURRENCY:
|
||||
case SCREEN_BTC_TICKER: {
|
||||
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
|
||||
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
||||
break;
|
||||
}
|
||||
case SCREEN_BLOCK_FEE_RATE: {
|
||||
WorkItem blockUpdate = {TASK_FEE_UPDATE, 0};
|
||||
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
||||
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;
|
||||
}
|
||||
}
|
||||
uint currentScreen = SCREEN_BLOCK_HEIGHT;
|
||||
uint currentCurrency = CURRENCY_USD;
|
||||
|
||||
if (eventSourceTaskHandle != NULL) xTaskNotifyGive(eventSourceTaskHandle);
|
||||
}
|
||||
|
||||
void ScreenHandler::setCurrentCurrency(char currency) {
|
||||
currentCurrency = currency;
|
||||
preferences.putUChar("lastCurrency", currency);
|
||||
}
|
||||
|
||||
bool ScreenHandler::isCurrencySpecific(uint screen) {
|
||||
switch (screen) {
|
||||
case SCREEN_BTC_TICKER:
|
||||
case SCREEN_SATS_PER_CURRENCY:
|
||||
case SCREEN_MARKET_CAP:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ScreenHandler::handleCurrencyRotation(bool forward) {
|
||||
if ((getDataSource() == BTCLOCK_SOURCE || getDataSource() == CUSTOM_SOURCE) && isCurrencySpecific(getCurrentScreen())) {
|
||||
std::vector<std::string> ac = getActiveCurrencies();
|
||||
if (ac.empty()) return false;
|
||||
|
||||
std::string curCode = getCurrencyCode(getCurrentCurrency());
|
||||
auto it = std::find(ac.begin(), ac.end(), curCode);
|
||||
|
||||
if (it == ac.end()) {
|
||||
// Current currency not found in active currencies - initialize based on direction
|
||||
setCurrentCurrency(getCurrencyChar(forward ? ac.front() : ac.back()));
|
||||
setCurrentScreen(getCurrentScreen());
|
||||
return true;
|
||||
} else if (forward && curCode != ac.back()) {
|
||||
// Moving forward and not at last currency
|
||||
setCurrentCurrency(getCurrencyChar(ac.at(std::distance(ac.begin(), it) + 1)));
|
||||
setCurrentScreen(getCurrentScreen());
|
||||
return true;
|
||||
} else if (!forward && curCode != ac.front()) {
|
||||
// Moving backward and not at first currency
|
||||
setCurrentCurrency(getCurrencyChar(ac.at(std::distance(ac.begin(), it) - 1)));
|
||||
setCurrentScreen(getCurrentScreen());
|
||||
return true;
|
||||
}
|
||||
// If we're at the last/first currency of current screen, let nextScreen/previousScreen handle it
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int ScreenHandler::findNextVisibleScreen(int currentScreen, bool forward) {
|
||||
std::vector<ScreenMapping> screenMappings = getScreenNameMap();
|
||||
int newScreen;
|
||||
|
||||
if (forward) {
|
||||
newScreen = (currentScreen < screenMappings.size() - 1) ?
|
||||
screenMappings[currentScreen + 1].value : screenMappings.front().value;
|
||||
} else {
|
||||
newScreen = (currentScreen > 0) ?
|
||||
screenMappings[currentScreen - 1].value : screenMappings.back().value;
|
||||
}
|
||||
|
||||
String key = "screen" + String(newScreen) + "Visible";
|
||||
while (!preferences.getBool(key.c_str(), true)) {
|
||||
currentScreen = findScreenIndexByValue(newScreen);
|
||||
if (forward) {
|
||||
newScreen = (currentScreen < screenMappings.size() - 1) ?
|
||||
screenMappings[currentScreen + 1].value : screenMappings.front().value;
|
||||
} else {
|
||||
newScreen = (currentScreen > 0) ?
|
||||
screenMappings[currentScreen - 1].value : screenMappings.back().value;
|
||||
}
|
||||
key = "screen" + String(newScreen) + "Visible";
|
||||
}
|
||||
|
||||
return newScreen;
|
||||
}
|
||||
|
||||
void ScreenHandler::nextScreen() {
|
||||
if (handleCurrencyRotation(true)) return;
|
||||
|
||||
int currentIndex = findScreenIndexByValue(getCurrentScreen());
|
||||
int nextScreen = findNextVisibleScreen(currentIndex, true);
|
||||
|
||||
// If moving from a currency-specific screen to another currency-specific screen
|
||||
// reset to first currency
|
||||
if (isCurrencySpecific(getCurrentScreen()) && isCurrencySpecific(nextScreen)) {
|
||||
std::vector<std::string> ac = getActiveCurrencies();
|
||||
if (!ac.empty()) {
|
||||
setCurrentCurrency(getCurrencyChar(ac.front()));
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentScreen(nextScreen);
|
||||
}
|
||||
|
||||
void ScreenHandler::previousScreen() {
|
||||
if (handleCurrencyRotation(false)) return;
|
||||
|
||||
int currentIndex = findScreenIndexByValue(getCurrentScreen());
|
||||
int prevScreen = findNextVisibleScreen(currentIndex, false);
|
||||
|
||||
// If moving from a currency-specific screen to another currency-specific screen
|
||||
// reset to last currency
|
||||
if (isCurrencySpecific(getCurrentScreen()) && isCurrencySpecific(prevScreen)) {
|
||||
std::vector<std::string> ac = getActiveCurrencies();
|
||||
if (!ac.empty()) {
|
||||
setCurrentCurrency(getCurrencyChar(ac.back()));
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentScreen(prevScreen);
|
||||
}
|
||||
|
||||
void ScreenHandler::showSystemStatusScreen() {
|
||||
std::array<String, NUM_SCREENS> sysStatusEpdContent;
|
||||
std::fill(sysStatusEpdContent.begin(), sysStatusEpdContent.end(), "");
|
||||
|
||||
String ipAddr = WiFi.localIP().toString();
|
||||
String subNet = WiFi.subnetMask().toString();
|
||||
|
||||
sysStatusEpdContent[0] = "IP/Subnet";
|
||||
|
||||
int ipAddrPos = 0;
|
||||
int subnetPos = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
sysStatusEpdContent[1 + i] = ipAddr.substring(0, ipAddr.indexOf('.')) +
|
||||
"/" + subNet.substring(0, subNet.indexOf('.'));
|
||||
ipAddrPos = ipAddr.indexOf('.') + 1;
|
||||
subnetPos = subNet.indexOf('.') + 1;
|
||||
ipAddr = ipAddr.substring(ipAddrPos);
|
||||
subNet = subNet.substring(subnetPos);
|
||||
}
|
||||
sysStatusEpdContent[NUM_SCREENS - 2] = "RAM/Status";
|
||||
|
||||
sysStatusEpdContent[NUM_SCREENS - 1] =
|
||||
String((int)round(ESP.getFreeHeap() / 1024)) + "/" +
|
||||
(int)round(ESP.getHeapSize() / 1024);
|
||||
setCurrentScreen(SCREEN_CUSTOM);
|
||||
setEpdContent(sysStatusEpdContent);
|
||||
}
|
||||
|
||||
// Keep these as free functions
|
||||
void workerTask(void *pvParameters) {
|
||||
WorkItem receivedItem;
|
||||
|
||||
while (1) {
|
||||
// Wait for a work item to be available in the queue
|
||||
if (xQueueReceive(workQueue, &receivedItem, portMAX_DELAY)) {
|
||||
uint currentScreenValue = ScreenHandler::getCurrentScreen();
|
||||
|
||||
// Process the work item based on its type
|
||||
switch (receivedItem.type) {
|
||||
case TASK_BITAXE_UPDATE: {
|
||||
if (currentScreenValue != SCREEN_BITAXE_HASHRATE &&
|
||||
currentScreenValue != SCREEN_BITAXE_BESTDIFF) break;
|
||||
|
||||
taskEpdContent = (currentScreenValue == SCREEN_BITAXE_HASHRATE) ?
|
||||
parseBitaxeHashRate(getBitAxeHashRate()) :
|
||||
if (getCurrentScreen() == SCREEN_BITAXE_HASHRATE) {
|
||||
taskEpdContent =
|
||||
parseBitaxeHashRate(getBitAxeHashRate());
|
||||
} else if (getCurrentScreen() == SCREEN_BITAXE_BESTDIFF) {
|
||||
taskEpdContent =
|
||||
parseBitaxeBestDiff(getBitaxeBestDiff());
|
||||
setEpdContent(taskEpdContent);
|
||||
break;
|
||||
}
|
||||
|
||||
case TASK_MINING_POOL_STATS_UPDATE: {
|
||||
if (currentScreenValue != SCREEN_MINING_POOL_STATS_HASHRATE &&
|
||||
currentScreenValue != SCREEN_MINING_POOL_STATS_EARNINGS) break;
|
||||
|
||||
taskEpdContent = (currentScreenValue == SCREEN_MINING_POOL_STATS_HASHRATE) ?
|
||||
parseMiningPoolStatsHashRate(getMiningPoolStatsHashRate(), *getMiningPool()) :
|
||||
parseMiningPoolStatsDailyEarnings(getMiningPoolStatsDailyEarnings(),
|
||||
getMiningPool()->getDailyEarningsLabel(), *getMiningPool());
|
||||
setEpdContent(taskEpdContent);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case TASK_PRICE_UPDATE: {
|
||||
uint currency = ScreenHandler::getCurrentCurrency();
|
||||
uint currency = getCurrentCurrency();
|
||||
uint price = getPrice(currency);
|
||||
|
||||
if (currentScreenValue == SCREEN_BTC_TICKER) {
|
||||
if (getCurrentScreen() == SCREEN_BTC_TICKER) {
|
||||
taskEpdContent = parsePriceData(price, currency, preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE),
|
||||
preferences.getBool("mowMode", DEFAULT_MOW_MODE),
|
||||
preferences.getBool("suffixShareDot", DEFAULT_SUFFIX_SHARE_DOT)
|
||||
);
|
||||
} else if (currentScreenValue == SCREEN_SATS_PER_CURRENCY) {
|
||||
} else if (getCurrentScreen() == SCREEN_SATS_PER_CURRENCY) {
|
||||
taskEpdContent = parseSatsPerCurrency(price, currency, preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL));
|
||||
} else {
|
||||
taskEpdContent =
|
||||
|
@ -259,27 +57,27 @@ void workerTask(void *pvParameters) {
|
|||
break;
|
||||
}
|
||||
case TASK_FEE_UPDATE: {
|
||||
if (currentScreenValue == SCREEN_BLOCK_FEE_RATE) {
|
||||
if (getCurrentScreen() == SCREEN_BLOCK_FEE_RATE) {
|
||||
taskEpdContent = parseBlockFees(static_cast<std::uint16_t>(getBlockMedianFee()));
|
||||
setEpdContent(taskEpdContent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TASK_BLOCK_UPDATE: {
|
||||
if (currentScreenValue != SCREEN_HALVING_COUNTDOWN) {
|
||||
if (getCurrentScreen() != SCREEN_HALVING_COUNTDOWN) {
|
||||
taskEpdContent = parseBlockHeight(getBlockHeight());
|
||||
} else {
|
||||
taskEpdContent = parseHalvingCountdown(getBlockHeight(), preferences.getBool("useBlkCountdown", DEFAULT_USE_BLOCK_COUNTDOWN));
|
||||
}
|
||||
|
||||
if (currentScreenValue == SCREEN_HALVING_COUNTDOWN ||
|
||||
currentScreenValue == SCREEN_BLOCK_HEIGHT) {
|
||||
if (getCurrentScreen() == SCREEN_HALVING_COUNTDOWN ||
|
||||
getCurrentScreen() == SCREEN_BLOCK_HEIGHT) {
|
||||
setEpdContent(taskEpdContent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TASK_TIME_UPDATE: {
|
||||
if (currentScreenValue == SCREEN_TIME) {
|
||||
if (getCurrentScreen() == SCREEN_TIME) {
|
||||
time_t currentTime;
|
||||
struct tm timeinfo;
|
||||
time(¤tTime);
|
||||
|
@ -315,13 +113,13 @@ void workerTask(void *pvParameters) {
|
|||
void taskScreenRotate(void *pvParameters) {
|
||||
for (;;) {
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
ScreenHandler::nextScreen();
|
||||
|
||||
nextScreen();
|
||||
}
|
||||
}
|
||||
|
||||
void setupTasks() {
|
||||
workQueue = xQueueCreate(WORK_QUEUE_SIZE, sizeof(WorkItem));
|
||||
loadStoredPrices();
|
||||
|
||||
xTaskCreate(workerTask, "workerTask", 4096, NULL, tskIDLE_PRIORITY,
|
||||
&workerTaskHandle);
|
||||
|
@ -332,10 +130,193 @@ void setupTasks() {
|
|||
waitUntilNoneBusy();
|
||||
|
||||
if (findScreenIndexByValue(preferences.getUInt("currentScreen", DEFAULT_CURRENT_SCREEN)) != -1)
|
||||
ScreenHandler::setCurrentScreen(preferences.getUInt("currentScreen", DEFAULT_CURRENT_SCREEN));
|
||||
setCurrentScreen(preferences.getUInt("currentScreen", DEFAULT_CURRENT_SCREEN));
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
vQueueDelete(workQueue);
|
||||
// Add any other cleanup needed
|
||||
uint getCurrentScreen() { return currentScreen; }
|
||||
|
||||
void setCurrentScreen(uint newScreen) {
|
||||
if (newScreen != SCREEN_CUSTOM) {
|
||||
preferences.putUInt("currentScreen", newScreen);
|
||||
}
|
||||
|
||||
currentScreen = newScreen;
|
||||
|
||||
switch (currentScreen) {
|
||||
case SCREEN_TIME: {
|
||||
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
|
||||
xQueueSend(workQueue, &timeUpdate, portMAX_DELAY);
|
||||
// xTaskNotifyGive(timeUpdateTaskHandle);
|
||||
break;
|
||||
}
|
||||
case SCREEN_HALVING_COUNTDOWN:
|
||||
case SCREEN_BLOCK_HEIGHT: {
|
||||
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
|
||||
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
||||
// xTaskNotifyGive(blockUpdateTaskHandle);
|
||||
break;
|
||||
}
|
||||
case SCREEN_MARKET_CAP:
|
||||
case SCREEN_SATS_PER_CURRENCY:
|
||||
case SCREEN_BTC_TICKER: {
|
||||
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
|
||||
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
||||
// xTaskNotifyGive(priceUpdateTaskHandle);
|
||||
break;
|
||||
}
|
||||
case SCREEN_BLOCK_FEE_RATE: {
|
||||
WorkItem blockUpdate = {TASK_FEE_UPDATE, 0};
|
||||
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
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";
|
||||
|
||||
while (!preferences.getBool(key.c_str(), true)) {
|
||||
currentIndex = findScreenIndexByValue(newCurrentScreen);
|
||||
if (currentIndex < screenMappings.size() - 1) {
|
||||
newCurrentScreen = (screenMappings[currentIndex + 1].value);
|
||||
} else {
|
||||
newCurrentScreen = screenMappings.front().value;
|
||||
}
|
||||
|
||||
key = "screen" + String(newCurrentScreen) + "Visible";
|
||||
}
|
||||
|
||||
setCurrentScreen(newCurrentScreen);
|
||||
}
|
||||
|
||||
void previousScreen() {
|
||||
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";
|
||||
|
||||
while (!preferences.getBool(key.c_str(), true)) {
|
||||
int currentIndex = findScreenIndexByValue(newCurrentScreen);
|
||||
if (currentIndex > 0) {
|
||||
newCurrentScreen = screenMappings[currentIndex - 1].value;
|
||||
} else {
|
||||
newCurrentScreen = screenMappings.back().value;
|
||||
}
|
||||
|
||||
key = "screen" + String(newCurrentScreen) + "Visible";
|
||||
}
|
||||
setCurrentScreen(newCurrentScreen);
|
||||
}
|
||||
|
||||
void showSystemStatusScreen() {
|
||||
std::array<String, NUM_SCREENS> sysStatusEpdContent;
|
||||
std::fill(sysStatusEpdContent.begin(), sysStatusEpdContent.end(), "");
|
||||
|
||||
|
||||
String ipAddr = WiFi.localIP().toString();
|
||||
String subNet = WiFi.subnetMask().toString();
|
||||
|
||||
sysStatusEpdContent[0] = "IP/Subnet";
|
||||
|
||||
int ipAddrPos = 0;
|
||||
int subnetPos = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
sysStatusEpdContent[1 + i] = ipAddr.substring(0, ipAddr.indexOf('.')) +
|
||||
"/" + subNet.substring(0, subNet.indexOf('.'));
|
||||
ipAddrPos = ipAddr.indexOf('.') + 1;
|
||||
subnetPos = subNet.indexOf('.') + 1;
|
||||
ipAddr = ipAddr.substring(ipAddrPos);
|
||||
subNet = subNet.substring(subnetPos);
|
||||
}
|
||||
sysStatusEpdContent[NUM_SCREENS - 2] = "RAM/Status";
|
||||
|
||||
sysStatusEpdContent[NUM_SCREENS - 1] =
|
||||
String((int)round(ESP.getFreeHeap() / 1024)) + "/" +
|
||||
(int)round(ESP.getHeapSize() / 1024);
|
||||
setCurrentScreen(SCREEN_CUSTOM);
|
||||
setEpdContent(sysStatusEpdContent);
|
||||
}
|
||||
|
||||
void setCurrentCurrency(char currency) {
|
||||
currentCurrency = currency;
|
||||
preferences.putUChar("lastCurrency", currency);
|
||||
}
|
||||
|
||||
uint getCurrentCurrency() {
|
||||
return currentCurrency;
|
||||
}
|
|
@ -6,15 +6,16 @@
|
|||
|
||||
#include <data_handler.hpp>
|
||||
#include <bitaxe_handler.hpp>
|
||||
#include "lib/mining_pool/mining_pool_stats_handler.hpp"
|
||||
|
||||
#include "lib/epd.hpp"
|
||||
#include "lib/shared.hpp"
|
||||
|
||||
#define WORK_QUEUE_SIZE 10
|
||||
|
||||
// extern TaskHandle_t priceUpdateTaskHandle;
|
||||
// extern TaskHandle_t blockUpdateTaskHandle;
|
||||
// extern TaskHandle_t timeUpdateTaskHandle;
|
||||
extern TaskHandle_t workerTaskHandle;
|
||||
extern TaskHandle_t taskScreenRotateTaskHandle;
|
||||
|
||||
extern QueueHandle_t workQueue;
|
||||
|
||||
typedef enum {
|
||||
|
@ -22,8 +23,7 @@ typedef enum {
|
|||
TASK_BLOCK_UPDATE,
|
||||
TASK_FEE_UPDATE,
|
||||
TASK_TIME_UPDATE,
|
||||
TASK_BITAXE_UPDATE,
|
||||
TASK_MINING_POOL_STATS_UPDATE
|
||||
TASK_BITAXE_UPDATE
|
||||
} TaskType;
|
||||
|
||||
typedef struct {
|
||||
|
@ -31,26 +31,24 @@ typedef struct {
|
|||
char data;
|
||||
} WorkItem;
|
||||
|
||||
class ScreenHandler {
|
||||
private:
|
||||
static uint currentScreen;
|
||||
static uint currentCurrency;
|
||||
|
||||
public:
|
||||
static uint getCurrentScreen() { return currentScreen; }
|
||||
static uint getCurrentCurrency() { return currentCurrency; }
|
||||
static void setCurrentScreen(uint newScreen);
|
||||
static void setCurrentCurrency(char currency);
|
||||
static void nextScreen();
|
||||
static void previousScreen();
|
||||
static void showSystemStatusScreen();
|
||||
static bool isCurrencySpecific(uint screen);
|
||||
static bool handleCurrencyRotation(bool forward);
|
||||
static int findNextVisibleScreen(int currentScreen, bool forward);
|
||||
};
|
||||
|
||||
// Keep as free functions since they deal with FreeRTOS tasks
|
||||
void workerTask(void *pvParameters);
|
||||
uint getCurrentScreen();
|
||||
void setCurrentScreen(uint newScreen);
|
||||
void nextScreen();
|
||||
void previousScreen();
|
||||
|
||||
void showSystemStatusScreen();
|
||||
|
||||
|
||||
|
||||
// void taskPriceUpdate(void *pvParameters);
|
||||
// void taskBlockUpdate(void *pvParameters);
|
||||
// void taskTimeUpdate(void *pvParameters);
|
||||
void taskScreenRotate(void *pvParameters);
|
||||
|
||||
|
||||
|
||||
void setupTasks();
|
||||
void cleanup();
|
||||
void setCurrentCurrency(char currency);
|
||||
|
||||
uint getCurrentCurrency();
|
|
@ -152,31 +152,3 @@ String calculateSHA256(WiFiClient *stream, size_t contentLength) {
|
|||
// 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;
|
||||
}
|
||||
}
|
|
@ -12,15 +12,12 @@
|
|||
#include <mbedtls/md.h>
|
||||
#include "esp_crt_bundle.h"
|
||||
#include <Update.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <utils.hpp>
|
||||
|
||||
#include "defaults.hpp"
|
||||
|
||||
#define USER_AGENT "BTClock/3.0"
|
||||
|
||||
extern MCP23017 mcp1;
|
||||
#ifdef IS_BTCLOCK_V8
|
||||
extern MCP23017 mcp2;
|
||||
|
@ -45,16 +42,24 @@ const PROGMEM int SCREEN_BLOCK_FEE_RATE = 6;
|
|||
const PROGMEM int SCREEN_SATS_PER_CURRENCY = 10;
|
||||
|
||||
const PROGMEM int SCREEN_BTC_TICKER = 20;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_USD = 20;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_EUR = 21;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_GBP = 22;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_JPY = 23;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_AUD = 24;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_CAD = 25;
|
||||
|
||||
const PROGMEM int SCREEN_MARKET_CAP = 30;
|
||||
|
||||
const PROGMEM int SCREEN_MINING_POOL_STATS_HASHRATE = 70;
|
||||
const PROGMEM int SCREEN_MINING_POOL_STATS_EARNINGS = 71;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_USD = 30;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_EUR = 31;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_GBP = 32;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_JPY = 33;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_AUD = 34;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_CAD = 35;
|
||||
|
||||
const PROGMEM int SCREEN_BITAXE_HASHRATE = 80;
|
||||
const PROGMEM int SCREEN_BITAXE_BESTDIFF = 81;
|
||||
|
||||
|
||||
const PROGMEM int SCREEN_COUNTDOWN = 98;
|
||||
const PROGMEM int SCREEN_CUSTOM = 99;
|
||||
const int SCREEN_COUNT = 7;
|
||||
|
@ -87,35 +92,3 @@ struct ScreenMapping {
|
|||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
struct Converter<std::array<String, N>> {
|
||||
static void toJson(const std::array<String, N>& src, JsonVariant dst) {
|
||||
JsonArray array = dst.to<JsonArray>();
|
||||
for (const String& 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;
|
||||
};
|
|
@ -72,10 +72,6 @@ void IRAM_ATTR minuteTimerISR(void *arg) {
|
|||
vTaskNotifyGiveFromISR(bitaxeFetchTaskHandle, &xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
if (miningPoolStatsFetchTaskHandle != NULL) {
|
||||
vTaskNotifyGiveFromISR(miningPoolStatsFetchTaskHandle, &xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
if (xHigherPriorityTaskWoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
|
|
@ -8,36 +8,25 @@ namespace V2Notify
|
|||
|
||||
TaskHandle_t v2NotifyTaskHandle;
|
||||
|
||||
String currentHostname;
|
||||
|
||||
void setupV2Notify()
|
||||
{
|
||||
String hostname = "ws.btclock.dev";
|
||||
if (getDataSource() == CUSTOM_SOURCE)
|
||||
if (preferences.getBool("stagingSource", DEFAULT_STAGING_SOURCE))
|
||||
{
|
||||
Serial.println(F("Connecting to custom source"));
|
||||
hostname = preferences.getString("ceEndpoint", DEFAULT_CUSTOM_ENDPOINT);
|
||||
bool useSSL = !preferences.getBool("ceDisableSSL", DEFAULT_CUSTOM_ENDPOINT_DISABLE_SSL);
|
||||
|
||||
if (useSSL) {
|
||||
webSocket.beginSSL(hostname, 443, "/api/v2/ws");
|
||||
} else {
|
||||
webSocket.begin(hostname, 80, "/api/v2/ws");
|
||||
}
|
||||
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.beginSSL(hostname, 443, "/api/v2/ws");
|
||||
webSocket.onEvent(V2Notify::onWebsocketV2Event);
|
||||
webSocket.setReconnectInterval(5000);
|
||||
webSocket.enableHeartbeat(15000, 3000, 2);
|
||||
|
||||
V2Notify::setupV2NotifyTask();
|
||||
|
||||
currentHostname = hostname;
|
||||
}
|
||||
|
||||
void onWebsocketV2Event(WStype_t type, uint8_t *payload, size_t length)
|
||||
|
@ -45,14 +34,11 @@ namespace V2Notify
|
|||
switch (type)
|
||||
{
|
||||
case WStype_DISCONNECTED:
|
||||
Serial.print(F("[WSc] Disconnected!\n"));
|
||||
Serial.printf("[WSc] Disconnected!\n");
|
||||
break;
|
||||
case WStype_CONNECTED:
|
||||
{
|
||||
Serial.print(F("[WSc] Connected to "));
|
||||
Serial.print(currentHostname);
|
||||
Serial.print(F(": "));
|
||||
Serial.println((char *)payload);
|
||||
Serial.printf("[WSc] Connected to url: %s\n", payload);
|
||||
|
||||
JsonDocument response;
|
||||
|
||||
|
@ -95,8 +81,7 @@ namespace V2Notify
|
|||
break;
|
||||
}
|
||||
case WStype_TEXT:
|
||||
Serial.print(F("[WSc] get text: "));
|
||||
Serial.println((char *)payload);
|
||||
Serial.printf("[WSc] get text: %s\n", payload);
|
||||
|
||||
// send message to server
|
||||
// webSocket.sendTXT("message here");
|
||||
|
|
|
@ -1,38 +1,26 @@
|
|||
#include "webserver.hpp"
|
||||
|
||||
static const char* JSON_CONTENT = "application/json";
|
||||
|
||||
static const char *const PROGMEM strSettings[] = {
|
||||
"hostnamePrefix", "mempoolInstance", "nostrPubKey", "nostrRelay", "bitaxeHostname", "miningPoolName", "miningPoolUser", "nostrZapPubkey", "httpAuthUser", "httpAuthPass", "gitReleaseUrl", "poolLogosUrl", "ceEndpoint", "fontName"};
|
||||
|
||||
static const char *const PROGMEM uintSettings[] = {"minSecPriceUpd", "fullRefreshMin", "ledBrightness", "flMaxBrightness", "flEffectDelay", "luxLightToggle", "wpTimeout"};
|
||||
|
||||
static const char *const PROGMEM boolSettings[] = {"ledTestOnPower", "ledFlashOnUpd",
|
||||
"mdnsEnabled", "otaEnabled", "stealFocus",
|
||||
"mcapBigChar", "useSatsSymbol", "useBlkCountdown",
|
||||
"suffixPrice", "disableLeds",
|
||||
"mowMode", "suffixShareDot", "flOffWhenDark",
|
||||
"flAlwaysOn", "flDisable", "flFlashOnUpd",
|
||||
"mempoolSecure", "bitaxeEnabled",
|
||||
"miningPoolStats", "verticalDesc",
|
||||
"nostrZapNotify", "httpAuthEnabled",
|
||||
"enableDebugLog", "ceDisableSSL", "dndEnabled", "dndTimeBasedEnabled"};
|
||||
|
||||
AsyncWebServer server(80);
|
||||
AsyncEventSource events("/events");
|
||||
TaskHandle_t eventSourceTaskHandle;
|
||||
|
||||
#define HTTP_OK 200
|
||||
#define HTTP_BAD_REQUEST 400
|
||||
|
||||
void setupWebserver()
|
||||
{
|
||||
events.onConnect([](AsyncEventSourceClient *client)
|
||||
{ client->send("welcome", NULL, millis(), 1000); });
|
||||
server.addHandler(&events);
|
||||
|
||||
// server.ad.
|
||||
// server.serveStatic("/css", LittleFS, "/css/");
|
||||
// server.serveStatic("/fonts", LittleFS, "/fonts/");
|
||||
// server.serveStatic("/build", LittleFS, "/build");
|
||||
// server.serveStatic("/swagger.json", LittleFS, "/swagger.json");
|
||||
// server.serveStatic("/api.html", LittleFS, "/api.html");
|
||||
// server.serveStatic("/fs_hash.txt", LittleFS, "/fs_hash.txt");
|
||||
|
||||
AsyncStaticWebHandler &staticHandler = server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html");
|
||||
|
||||
|
||||
server.rewrite("/convert", "/");
|
||||
server.rewrite("/api", "/");
|
||||
|
||||
|
@ -43,6 +31,7 @@ void setupWebserver()
|
|||
preferences.getString("httpAuthPass", DEFAULT_HTTP_AUTH_PASSWORD));
|
||||
}
|
||||
// server.on("/", HTTP_GET, onIndex);
|
||||
|
||||
server.on("/api/status", HTTP_GET, onApiStatus);
|
||||
server.on("/api/system_status", HTTP_GET, onApiSystemStatus);
|
||||
server.on("/api/wifi_set_tx_power", HTTP_GET, onApiSetWifiTxPower);
|
||||
|
@ -62,8 +51,8 @@ void setupWebserver()
|
|||
|
||||
server.on("/api/show/text", HTTP_GET, onApiShowText);
|
||||
|
||||
server.on("/api/screen/next", HTTP_GET, onApiScreenControl);
|
||||
server.on("/api/screen/previous", HTTP_GET, onApiScreenControl);
|
||||
server.on("/api/screen/next", HTTP_GET, onApiScreenNext);
|
||||
server.on("/api/screen/previous", HTTP_GET, onApiScreenPrevious);
|
||||
|
||||
AsyncCallbackJsonWebHandler *settingsPatchHandler =
|
||||
new AsyncCallbackJsonWebHandler("/api/json/settings", onApiSettingsPatch);
|
||||
|
@ -116,10 +105,6 @@ void setupWebserver()
|
|||
server.addRewrite(new OneParamRewrite("/api/show/number/{number}",
|
||||
"/api/show/text?t={text}"));
|
||||
|
||||
server.on("/api/dnd/status", HTTP_GET, onApiDNDStatus);
|
||||
server.on("/api/dnd/enable", HTTP_POST, onApiDNDEnable);
|
||||
server.on("/api/dnd/disable", HTTP_POST, onApiDNDDisable);
|
||||
|
||||
server.onNotFound(onNotFound);
|
||||
|
||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
|
||||
|
@ -227,16 +212,45 @@ void asyncFileUpdateHandler(AsyncWebServerRequest *request, String filename, siz
|
|||
void asyncFirmwareUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final)
|
||||
{
|
||||
asyncFileUpdateHandler(request, filename, index, data, len, final, U_FLASH);
|
||||
|
||||
// if (!index)
|
||||
// {
|
||||
// Serial.printf("Update Start: %s\n", filename.c_str());
|
||||
|
||||
// // Update.runAsync(true);
|
||||
// if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000))
|
||||
// {
|
||||
// Update.printError(Serial);
|
||||
// }
|
||||
// }
|
||||
// if (!Update.hasError())
|
||||
// {
|
||||
// if (Update.write(data, len) != len)
|
||||
// {
|
||||
// Update.printError(Serial);
|
||||
// }
|
||||
// }
|
||||
// if (final)
|
||||
// {
|
||||
// if (Update.end(true))
|
||||
// {
|
||||
// Serial.printf("Update Success: %uB\n", index + len);
|
||||
// onApiRestart(request);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Update.printError(Serial);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
JsonDocument getStatusObject()
|
||||
{
|
||||
JsonDocument root;
|
||||
|
||||
root["currentScreen"] = ScreenHandler::getCurrentScreen();
|
||||
root["currentScreen"] = getCurrentScreen();
|
||||
root["numScreens"] = NUM_SCREENS;
|
||||
root["timerRunning"] = isTimerActive();
|
||||
root["isOTAUpdating"] = getIsOTAUpdating();
|
||||
root["espUptime"] = esp_timer_get_time() / 1000000;
|
||||
// root["currentPrice"] = getPrice();
|
||||
// root["currentBlockHeight"] = getBlockHeight();
|
||||
|
@ -254,7 +268,7 @@ JsonDocument getStatusObject()
|
|||
conStatus["nostr"] = nostrConnected();
|
||||
|
||||
root["rssi"] = WiFi.RSSI();
|
||||
root["currency"] = getCurrencyCode(ScreenHandler::getCurrentCurrency());
|
||||
root["currency"] = getCurrencyCode(getCurrentCurrency());
|
||||
#ifdef HAS_FRONTLIGHT
|
||||
std::vector<uint16_t> statuses = frontlightGetStatus();
|
||||
uint16_t arr[NUM_SCREENS];
|
||||
|
@ -269,15 +283,6 @@ JsonDocument getStatusObject()
|
|||
}
|
||||
#endif
|
||||
|
||||
// Add DND status
|
||||
root["dnd"]["enabled"] = dndEnabled;
|
||||
root["dnd"]["timeBasedEnabled"] = dndTimeBasedEnabled;
|
||||
root["dnd"]["startTime"] = String(dndTimeRange.startHour) + ":" +
|
||||
(dndTimeRange.startMinute < 10 ? "0" : "") + String(dndTimeRange.startMinute);
|
||||
root["dnd"]["endTime"] = String(dndTimeRange.endHour) + ":" +
|
||||
(dndTimeRange.endMinute < 10 ? "0" : "") + String(dndTimeRange.endMinute);
|
||||
root["dnd"]["active"] = isDNDActive();
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
|
@ -290,6 +295,7 @@ JsonDocument getLedStatusObject()
|
|||
for (uint i = 0; i < pixels.numPixels(); i++)
|
||||
{
|
||||
uint32_t pixColor = pixels.getPixelColor(pixels.numPixels() - i - 1);
|
||||
uint alpha = (pixColor >> 24) & 0xFF;
|
||||
uint red = (pixColor >> 16) & 0xFF;
|
||||
uint green = (pixColor >> 8) & 0xFF;
|
||||
uint blue = pixColor & 0xFF;
|
||||
|
@ -307,26 +313,25 @@ JsonDocument getLedStatusObject()
|
|||
return root;
|
||||
}
|
||||
|
||||
void eventSourceUpdate() {
|
||||
if (!events.count()) return;
|
||||
void eventSourceUpdate()
|
||||
{
|
||||
if (!events.count())
|
||||
return;
|
||||
JsonDocument root = getStatusObject();
|
||||
JsonArray data = root["data"].to<JsonArray>();
|
||||
|
||||
JsonDocument doc = getStatusObject();
|
||||
doc["leds"] = getLedStatusObject()["data"];
|
||||
root["leds"] = getLedStatusObject()["data"];
|
||||
|
||||
// Get current EPD content directly as array
|
||||
std::array<String, NUM_SCREENS> epdContent = getCurrentEpdContent();
|
||||
String epdContent[NUM_SCREENS];
|
||||
std::array<String, NUM_SCREENS> retEpdContent = getCurrentEpdContent();
|
||||
std::copy(std::begin(retEpdContent), std::end(retEpdContent), epdContent);
|
||||
|
||||
// Add EPD content arrays
|
||||
JsonArray data = doc["data"].to<JsonArray>();
|
||||
copyArray(epdContent, data);
|
||||
|
||||
// Copy array elements directly
|
||||
for(const auto& content : epdContent) {
|
||||
data.add(content);
|
||||
}
|
||||
String bufString;
|
||||
serializeJson(root, bufString);
|
||||
|
||||
String buffer;
|
||||
serializeJson(doc, buffer);
|
||||
events.send(buffer.c_str(), "status");
|
||||
events.send(bufString.c_str(), "status");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -336,22 +341,21 @@ void eventSourceUpdate() {
|
|||
void onApiStatus(AsyncWebServerRequest *request)
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
|
||||
JsonDocument root = getStatusObject();
|
||||
|
||||
// Get current EPD content directly as array
|
||||
std::array<String, NUM_SCREENS> epdContent = getCurrentEpdContent();
|
||||
|
||||
// Add EPD content arrays
|
||||
JsonArray data = root["data"].to<JsonArray>();
|
||||
|
||||
// Copy array elements directly
|
||||
for(const auto& content : epdContent) {
|
||||
data.add(content);
|
||||
}
|
||||
JsonArray rendered = root["rendered"].to<JsonArray>();
|
||||
String epdContent[NUM_SCREENS];
|
||||
|
||||
root["leds"] = getLedStatusObject()["data"];
|
||||
|
||||
std::array<String, NUM_SCREENS> retEpdContent = getCurrentEpdContent();
|
||||
|
||||
std::copy(std::begin(retEpdContent), std::end(retEpdContent), epdContent);
|
||||
|
||||
copyArray(epdContent, data);
|
||||
copyArray(epdContent, rendered);
|
||||
serializeJson(root, *response);
|
||||
|
||||
request->send(response);
|
||||
|
@ -364,7 +368,7 @@ void onApiStatus(AsyncWebServerRequest *request)
|
|||
void onApiActionPause(AsyncWebServerRequest *request)
|
||||
{
|
||||
setTimerActive(false);
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -374,7 +378,7 @@ void onApiActionPause(AsyncWebServerRequest *request)
|
|||
void onApiActionTimerRestart(AsyncWebServerRequest *request)
|
||||
{
|
||||
setTimerActive(true);
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -388,7 +392,7 @@ void onApiFullRefresh(AsyncWebServerRequest *request)
|
|||
|
||||
setEpdContent(newEpdContent, true);
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -401,23 +405,30 @@ void onApiShowScreen(AsyncWebServerRequest *request)
|
|||
{
|
||||
const AsyncWebParameter *p = request->getParam("s");
|
||||
uint currentScreen = p->value().toInt();
|
||||
ScreenHandler::setCurrentScreen(currentScreen);
|
||||
setCurrentScreen(currentScreen);
|
||||
}
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Api
|
||||
* @Path("/api/screen/next")
|
||||
*/
|
||||
void onApiScreenControl(AsyncWebServerRequest *request) {
|
||||
const String& action = request->url();
|
||||
if (action.endsWith("/next")) {
|
||||
ScreenHandler::nextScreen();
|
||||
} else if (action.endsWith("/previous")) {
|
||||
ScreenHandler::previousScreen();
|
||||
}
|
||||
request->send(HTTP_OK);
|
||||
void onApiScreenNext(AsyncWebServerRequest *request)
|
||||
{
|
||||
nextScreen();
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Api
|
||||
* @Path("/api/screen/previous")
|
||||
*/
|
||||
void onApiScreenPrevious(AsyncWebServerRequest *request)
|
||||
{
|
||||
previousScreen();
|
||||
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onApiShowText(AsyncWebServerRequest *request)
|
||||
|
@ -436,8 +447,8 @@ void onApiShowText(AsyncWebServerRequest *request)
|
|||
|
||||
setEpdContent(textEpdContent);
|
||||
}
|
||||
ScreenHandler::setCurrentScreen(SCREEN_CUSTOM);
|
||||
request->send(HTTP_OK);
|
||||
setCurrentScreen(SCREEN_CUSTOM);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onApiShowTextAdvanced(AsyncWebServerRequest *request, JsonVariant &json)
|
||||
|
@ -454,8 +465,8 @@ void onApiShowTextAdvanced(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
|
||||
setEpdContent(epdContent);
|
||||
|
||||
ScreenHandler::setCurrentScreen(SCREEN_CUSTOM);
|
||||
request->send(HTTP_OK);
|
||||
setCurrentScreen(SCREEN_CUSTOM);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
||||
|
@ -473,46 +484,49 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
|
||||
bool settingsChanged = true;
|
||||
|
||||
if (settings["invertedColor"].is<bool>())
|
||||
if (settings.containsKey("fgColor"))
|
||||
{
|
||||
bool inverted = settings["invertedColor"].as<bool>();
|
||||
preferences.putBool("invertedColor", inverted);
|
||||
if (inverted) {
|
||||
preferences.putUInt("fgColor", GxEPD_WHITE);
|
||||
preferences.putUInt("bgColor", GxEPD_BLACK);
|
||||
setFgColor(GxEPD_WHITE);
|
||||
setBgColor(GxEPD_BLACK);
|
||||
} else {
|
||||
preferences.putUInt("fgColor", GxEPD_BLACK);
|
||||
preferences.putUInt("bgColor", GxEPD_WHITE);
|
||||
setFgColor(GxEPD_BLACK);
|
||||
setBgColor(GxEPD_WHITE);
|
||||
String fgColor = settings["fgColor"].as<String>();
|
||||
preferences.putUInt("fgColor", strtol(fgColor.c_str(), NULL, 16));
|
||||
setFgColor(int(strtol(fgColor.c_str(), NULL, 16)));
|
||||
Serial.print(F("Setting foreground color to "));
|
||||
Serial.println(strtol(fgColor.c_str(), NULL, 16));
|
||||
settingsChanged = true;
|
||||
}
|
||||
Serial.printf("Setting invertedColor to %d\r\n", inverted);
|
||||
if (settings.containsKey("bgColor"))
|
||||
{
|
||||
String bgColor = settings["bgColor"].as<String>();
|
||||
|
||||
preferences.putUInt("bgColor", strtol(bgColor.c_str(), NULL, 16));
|
||||
setBgColor(int(strtol(bgColor.c_str(), NULL, 16)));
|
||||
Serial.print(F("Setting background color to "));
|
||||
Serial.println(bgColor.c_str());
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
if (settings["timePerScreen"].is<uint>())
|
||||
if (settings.containsKey("timePerScreen"))
|
||||
{
|
||||
preferences.putUInt("timerSeconds",
|
||||
settings["timePerScreen"].as<uint>() * 60);
|
||||
}
|
||||
|
||||
String strSettings[] = {"hostnamePrefix", "mempoolInstance", "nostrPubKey", "nostrRelay", "bitaxeHostname", "nostrZapPubkey", "httpAuthUser", "httpAuthPass", "gitReleaseUrl"};
|
||||
|
||||
for (String setting : strSettings)
|
||||
{
|
||||
if (settings[setting].is<String>())
|
||||
if (settings.containsKey(setting))
|
||||
{
|
||||
preferences.putString(setting.c_str(), settings[setting].as<String>());
|
||||
Serial.printf("Setting %s to %s\r\n", setting.c_str(),
|
||||
settings[setting].as<String>().c_str());
|
||||
settings[setting].as<String>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String uintSettings[] = {"minSecPriceUpd", "fullRefreshMin", "ledBrightness", "flMaxBrightness", "flEffectDelay", "luxLightToggle", "wpTimeout", "srcV2Currency"};
|
||||
|
||||
for (String setting : uintSettings)
|
||||
{
|
||||
if (settings[setting].is<uint>())
|
||||
if (settings.containsKey(setting))
|
||||
{
|
||||
preferences.putUInt(setting.c_str(), settings[setting].as<uint>());
|
||||
Serial.printf("Setting %s to %d\r\n", setting.c_str(),
|
||||
|
@ -520,7 +534,7 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
}
|
||||
}
|
||||
|
||||
if (settings["tzOffset"].is<int>())
|
||||
if (settings.containsKey("tzOffset"))
|
||||
{
|
||||
int gmtOffset = settings["tzOffset"].as<int>() * 60;
|
||||
size_t written = preferences.putInt("gmtOffset", gmtOffset);
|
||||
|
@ -528,17 +542,27 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
gmtOffset, settings["tzOffset"].as<int>(), written);
|
||||
}
|
||||
|
||||
String boolSettings[] = {"fetchEurPrice", "ledTestOnPower", "ledFlashOnUpd",
|
||||
"mdnsEnabled", "otaEnabled", "stealFocus",
|
||||
"mcapBigChar", "useSatsSymbol", "useBlkCountdown",
|
||||
"suffixPrice", "disableLeds", "ownDataSource",
|
||||
"mowMode", "suffixShareDot", "flOffWhenDark",
|
||||
"flAlwaysOn", "flDisable", "flFlashOnUpd",
|
||||
"mempoolSecure", "useNostr", "bitaxeEnabled",
|
||||
"verticalDesc",
|
||||
"nostrZapNotify", "stagingSource", "httpAuthEnabled"};
|
||||
|
||||
for (String setting : boolSettings)
|
||||
{
|
||||
if (settings[setting].is<bool>())
|
||||
if (settings.containsKey(setting))
|
||||
{
|
||||
preferences.putBool(setting.c_str(), settings[setting].as<bool>());
|
||||
preferences.putBool(setting.c_str(), settings[setting].as<boolean>());
|
||||
Serial.printf("Setting %s to %d\r\n", setting.c_str(),
|
||||
settings[setting].as<bool>());
|
||||
settings[setting].as<boolean>());
|
||||
}
|
||||
}
|
||||
|
||||
if (settings["screens"].is<JsonArray>())
|
||||
if (settings.containsKey("screens"))
|
||||
{
|
||||
for (JsonVariant screen : settings["screens"].as<JsonArray>())
|
||||
{
|
||||
|
@ -546,12 +570,12 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
uint id = s["id"].as<uint>();
|
||||
String key = "screen[" + String(id) + "]";
|
||||
String prefKey = "screen" + String(id) + "Visible";
|
||||
bool visible = s["enabled"].as<bool>();
|
||||
bool visible = s["enabled"].as<boolean>();
|
||||
preferences.putBool(prefKey.c_str(), visible);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings["actCurrencies"].is<JsonArray>())
|
||||
if (settings.containsKey("actCurrencies"))
|
||||
{
|
||||
String actCurrencies;
|
||||
|
||||
|
@ -565,10 +589,10 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
}
|
||||
|
||||
preferences.putString("actCurrencies", actCurrencies.c_str());
|
||||
Serial.printf("Set actCurrencies: %s\n", actCurrencies.c_str());
|
||||
Serial.printf("Set actCurrencies: %s\n", actCurrencies);
|
||||
}
|
||||
|
||||
if (settings["txPower"].is<int>())
|
||||
if (settings.containsKey("txPower"))
|
||||
{
|
||||
int txPower = settings["txPower"].as<int>();
|
||||
|
||||
|
@ -595,47 +619,7 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
}
|
||||
}
|
||||
|
||||
// Handle data source setting
|
||||
if (settings["dataSource"].is<uint8_t>()) {
|
||||
uint8_t dataSource = settings["dataSource"].as<uint8_t>();
|
||||
if (dataSource <= CUSTOM_SOURCE) { // Validate including custom source
|
||||
preferences.putUChar("dataSource", dataSource);
|
||||
Serial.printf("Setting dataSource to %d\r\n", dataSource);
|
||||
settingsChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle custom endpoint settings
|
||||
if (settings["customEndpoint"].is<String>()) {
|
||||
preferences.putString("customEndpoint", settings["customEndpoint"].as<String>());
|
||||
Serial.printf("Setting customEndpoint to %s\r\n", settings["customEndpoint"].as<String>().c_str());
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
if (settings["customEndpointDisableSSL"].is<bool>()) {
|
||||
preferences.putBool("customEndpointDisableSSL", settings["customEndpointDisableSSL"].as<bool>());
|
||||
Serial.printf("Setting customEndpointDisableSSL to %d\r\n", settings["customEndpointDisableSSL"].as<bool>());
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
// Handle DND settings
|
||||
if (settings.containsKey("dnd")) {
|
||||
JsonObject dndObj = settings["dnd"];
|
||||
if (dndObj.containsKey("timeBasedEnabled")) {
|
||||
setDNDTimeBasedEnabled(dndObj["timeBasedEnabled"].as<bool>());
|
||||
}
|
||||
if (dndObj.containsKey("startHour") && dndObj.containsKey("startMinute") &&
|
||||
dndObj.containsKey("endHour") && dndObj.containsKey("endMinute")) {
|
||||
setDNDTimeRange(
|
||||
dndObj["startHour"].as<uint8_t>(),
|
||||
dndObj["startMinute"].as<uint8_t>(),
|
||||
dndObj["endHour"].as<uint8_t>(),
|
||||
dndObj["endMinute"].as<uint8_t>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
if (settingsChanged)
|
||||
{
|
||||
queueLedEffect(LED_FLASH_SUCCESS);
|
||||
|
@ -644,24 +628,21 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
|
||||
void onApiRestart(AsyncWebServerRequest *request)
|
||||
{
|
||||
request->onDisconnect([]() {
|
||||
delay(500);
|
||||
|
||||
noInterrupts();
|
||||
esp_restart();
|
||||
});
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
|
||||
if (events.count())
|
||||
events.send("closing");
|
||||
|
||||
delay(500);
|
||||
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
void onApiIdentify(AsyncWebServerRequest *request)
|
||||
{
|
||||
queueLedEffect(LED_FLASH_IDENTIFY);
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -682,7 +663,8 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
|||
|
||||
JsonDocument root;
|
||||
root["numScreens"] = NUM_SCREENS;
|
||||
root["invertedColor"] = preferences.getBool("invertedColor", getFgColor() == GxEPD_WHITE);
|
||||
root["fgColor"] = getFgColor();
|
||||
root["bgColor"] = getBgColor();
|
||||
root["timerSeconds"] = getTimerSeconds();
|
||||
root["timerRunning"] = isTimerActive();
|
||||
root["minSecPriceUpd"] = preferences.getUInt(
|
||||
|
@ -691,26 +673,10 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
|||
preferences.getUInt("fullRefreshMin", DEFAULT_MINUTES_FULL_REFRESH);
|
||||
root["wpTimeout"] = preferences.getUInt("wpTimeout", DEFAULT_WP_TIMEOUT);
|
||||
root["tzOffset"] = preferences.getInt("gmtOffset", DEFAULT_TIME_OFFSET_SECONDS) / 60;
|
||||
|
||||
// Add data source settings
|
||||
root["dataSource"] = preferences.getUChar("dataSource", DEFAULT_DATA_SOURCE);
|
||||
|
||||
// Mempool settings (only used for THIRD_PARTY_SOURCE)
|
||||
root["mempoolInstance"] = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
||||
root["mempoolInstance"] =
|
||||
preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
||||
root["mempoolSecure"] = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE);
|
||||
|
||||
// Nostr settings (used for NOSTR_SOURCE or when zapNotify is enabled)
|
||||
root["nostrPubKey"] = preferences.getString("nostrPubKey", DEFAULT_NOSTR_NPUB);
|
||||
root["nostrRelay"] = preferences.getString("nostrRelay", DEFAULT_NOSTR_RELAY);
|
||||
root["nostrZapNotify"] = preferences.getBool("nostrZapNotify", DEFAULT_ZAP_NOTIFY_ENABLED);
|
||||
root["nostrZapPubkey"] = preferences.getString("nostrZapPubkey", DEFAULT_ZAP_NOTIFY_PUBKEY);
|
||||
root["ledFlashOnZap"] = preferences.getBool("ledFlashOnZap", DEFAULT_LED_FLASH_ON_ZAP);
|
||||
root["fontName"] = preferences.getString("fontName", DEFAULT_FONT_NAME);
|
||||
root["availableFonts"] = FontNames::getAvailableFonts();
|
||||
// Custom endpoint settings (only used for CUSTOM_SOURCE)
|
||||
root["customEndpoint"] = preferences.getString("customEndpoint", DEFAULT_CUSTOM_ENDPOINT);
|
||||
root["customEndpointDisableSSL"] = preferences.getBool("customEndpointDisableSSL", DEFAULT_CUSTOM_ENDPOINT_DISABLE_SSL);
|
||||
|
||||
root["useNostr"] = preferences.getBool("useNostr", DEFAULT_USE_NOSTR);
|
||||
root["ledTestOnPower"] = preferences.getBool("ledTestOnPower", DEFAULT_LED_TEST_ON_POWER);
|
||||
root["ledFlashOnUpd"] = preferences.getBool("ledFlashOnUpd", DEFAULT_LED_FLASH_ON_UPD);
|
||||
root["ledBrightness"] = preferences.getUInt("ledBrightness", DEFAULT_LED_BRIGHTNESS);
|
||||
|
@ -718,6 +684,7 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
|||
root["mcapBigChar"] = preferences.getBool("mcapBigChar", DEFAULT_MCAP_BIG_CHAR);
|
||||
root["mdnsEnabled"] = preferences.getBool("mdnsEnabled", DEFAULT_MDNS_ENABLED);
|
||||
root["otaEnabled"] = preferences.getBool("otaEnabled", DEFAULT_OTA_ENABLED);
|
||||
// root["fetchEurPrice"] = preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE);
|
||||
root["useSatsSymbol"] = preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL);
|
||||
root["useBlkCountdown"] = preferences.getBool("useBlkCountdown", DEFAULT_USE_BLOCK_COUNTDOWN);
|
||||
root["suffixPrice"] = preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE);
|
||||
|
@ -726,25 +693,31 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
|||
root["verticalDesc"] = preferences.getBool("verticalDesc", DEFAULT_VERTICAL_DESC);
|
||||
|
||||
root["suffixShareDot"] = preferences.getBool("suffixShareDot", DEFAULT_SUFFIX_SHARE_DOT);
|
||||
root["enableDebugLog"] = preferences.getBool("enableDebugLog", DEFAULT_ENABLE_DEBUG_LOG);
|
||||
|
||||
root["hostnamePrefix"] = preferences.getString("hostnamePrefix", DEFAULT_HOSTNAME_PREFIX);
|
||||
root["hostname"] = getMyHostname();
|
||||
root["ip"] = WiFi.localIP();
|
||||
root["txPower"] = WiFi.getTxPower();
|
||||
root["ownDataSource"] = preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE);
|
||||
root["stagingSource"] = preferences.getBool("stagingSource", DEFAULT_STAGING_SOURCE);
|
||||
root["srcV2Currency"] = preferences.getChar("srcV2Currency", DEFAULT_V2_SOURCE_CURRENCY);
|
||||
|
||||
root["nostrPubKey"] = preferences.getString("nostrPubKey", DEFAULT_NOSTR_NPUB);
|
||||
root["nostrRelay"] = preferences.getString("nostrRelay", DEFAULT_NOSTR_RELAY);
|
||||
|
||||
root["nostrZapNotify"] = preferences.getBool("nostrZapNotify", DEFAULT_ZAP_NOTIFY_ENABLED);
|
||||
root["nostrZapPubkey"] = preferences.getString("nostrZapPubkey", DEFAULT_ZAP_NOTIFY_PUBKEY);
|
||||
root["ledFlashOnZap"] = preferences.getBool("ledFlashOnZap", DEFAULT_LED_FLASH_ON_ZAP);
|
||||
|
||||
root["gitReleaseUrl"] = preferences.getString("gitReleaseUrl", DEFAULT_GIT_RELEASE_URL);
|
||||
|
||||
root["bitaxeEnabled"] = preferences.getBool("bitaxeEnabled", DEFAULT_BITAXE_ENABLED);
|
||||
root["bitaxeHostname"] = preferences.getString("bitaxeHostname", DEFAULT_BITAXE_HOSTNAME);
|
||||
|
||||
root["miningPoolStats"] = preferences.getBool("miningPoolStats", DEFAULT_MINING_POOL_STATS_ENABLED);
|
||||
root["miningPoolName"] = preferences.getString("miningPoolName", DEFAULT_MINING_POOL_NAME);
|
||||
root["miningPoolUser"] = preferences.getString("miningPoolUser", DEFAULT_MINING_POOL_USER);
|
||||
root["availablePools"] = PoolFactory::getAvailablePools();
|
||||
root["httpAuthEnabled"] = preferences.getBool("httpAuthEnabled", DEFAULT_HTTP_AUTH_ENABLED);
|
||||
root["httpAuthUser"] = preferences.getString("httpAuthUser", DEFAULT_HTTP_AUTH_USERNAME);
|
||||
root["httpAuthPass"] = preferences.getString("httpAuthPass", DEFAULT_HTTP_AUTH_PASSWORD);
|
||||
|
||||
#ifdef HAS_FRONTLIGHT
|
||||
root["hasFrontlight"] = true;
|
||||
root["flDisable"] = preferences.getBool("flDisable");
|
||||
|
@ -778,8 +751,17 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
|||
#endif
|
||||
JsonArray screens = root["screens"].to<JsonArray>();
|
||||
|
||||
root["actCurrencies"] = getActiveCurrencies();
|
||||
root["availableCurrencies"] = getAvailableCurrencies();
|
||||
JsonArray actCurrencies = root["actCurrencies"].to<JsonArray>();
|
||||
for (const auto &str : getActiveCurrencies())
|
||||
{
|
||||
actCurrencies.add(str);
|
||||
}
|
||||
|
||||
JsonArray availableCurrencies = root["availableCurrencies"].to<JsonArray>();
|
||||
for (const auto &str : getAvailableCurrencies())
|
||||
{
|
||||
availableCurrencies.add(str);
|
||||
}
|
||||
|
||||
std::vector<ScreenMapping> screenNameMap = getScreenNameMap();
|
||||
|
||||
|
@ -792,20 +774,8 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
|||
o["enabled"] = preferences.getBool(key.c_str(), true);
|
||||
}
|
||||
|
||||
root["poolLogosUrl"] = preferences.getString("poolLogosUrl", DEFAULT_MINING_POOL_LOGOS_URL);
|
||||
root["ceEndpoint"] = preferences.getString("ceEndpoint", DEFAULT_CUSTOM_ENDPOINT);
|
||||
root["ceDisableSSL"] = preferences.getBool("ceDisableSSL", DEFAULT_CUSTOM_ENDPOINT_DISABLE_SSL);
|
||||
|
||||
// Add DND settings
|
||||
root["dnd"]["enabled"] = dndEnabled;
|
||||
root["dnd"]["timeBasedEnabled"] = dndTimeBasedEnabled;
|
||||
root["dnd"]["startHour"] = dndTimeRange.startHour;
|
||||
root["dnd"]["startMinute"] = dndTimeRange.startMinute;
|
||||
root["dnd"]["endHour"] = dndTimeRange.endHour;
|
||||
root["dnd"]["endMinute"] = dndTimeRange.endMinute;
|
||||
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
serializeJson(root, *response);
|
||||
|
||||
request->send(response);
|
||||
|
@ -817,9 +787,8 @@ bool processEpdColorSettings(AsyncWebServerRequest *request)
|
|||
if (request->hasParam("fgColor", true))
|
||||
{
|
||||
const AsyncWebParameter *fgColor = request->getParam("fgColor", true);
|
||||
uint32_t color = strtol(fgColor->value().c_str(), NULL, 16);
|
||||
preferences.putUInt("fgColor", color);
|
||||
setFgColor(color);
|
||||
preferences.putUInt("fgColor", strtol(fgColor->value().c_str(), NULL, 16));
|
||||
setFgColor(int(strtol(fgColor->value().c_str(), NULL, 16)));
|
||||
// Serial.print(F("Setting foreground color to "));
|
||||
// Serial.println(fgColor->value().c_str());
|
||||
settingsChanged = true;
|
||||
|
@ -828,9 +797,8 @@ bool processEpdColorSettings(AsyncWebServerRequest *request)
|
|||
{
|
||||
const AsyncWebParameter *bgColor = request->getParam("bgColor", true);
|
||||
|
||||
uint32_t color = strtol(bgColor->value().c_str(), NULL, 16);
|
||||
preferences.putUInt("bgColor", color);
|
||||
setBgColor(color);
|
||||
preferences.putUInt("bgColor", strtol(bgColor->value().c_str(), NULL, 16));
|
||||
setBgColor(int(strtol(bgColor->value().c_str(), NULL, 16)));
|
||||
// Serial.print(F("Setting background color to "));
|
||||
// Serial.println(bgColor->value().c_str());
|
||||
settingsChanged = true;
|
||||
|
@ -842,7 +810,7 @@ bool processEpdColorSettings(AsyncWebServerRequest *request)
|
|||
void onApiSystemStatus(AsyncWebServerRequest *request)
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
|
||||
JsonDocument root;
|
||||
|
||||
|
@ -850,9 +818,6 @@ void onApiSystemStatus(AsyncWebServerRequest *request)
|
|||
root["espHeapSize"] = ESP.getHeapSize();
|
||||
root["espFreePsram"] = ESP.getFreePsram();
|
||||
root["espPsramSize"] = ESP.getPsramSize();
|
||||
root["fsUsedBytes"] = LittleFS.usedBytes();
|
||||
root["fsTotalBytes"] = LittleFS.totalBytes();
|
||||
|
||||
root["rssi"] = WiFi.RSSI();
|
||||
root["txPower"] = WiFi.getTxPower();
|
||||
|
||||
|
@ -884,19 +849,19 @@ void onApiSetWifiTxPower(AsyncWebServerRequest *request)
|
|||
if (WiFi.setTxPower(static_cast<wifi_power_t>(txPower)))
|
||||
{
|
||||
preferences.putInt("txPower", txPower);
|
||||
request->send(HTTP_OK, "application/json", "{\"setTxPower\": \"ok\"}");
|
||||
request->send(200, "application/json", "{\"setTxPower\": \"ok\"}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return request->send(HTTP_BAD_REQUEST);
|
||||
return request->send(400);
|
||||
}
|
||||
|
||||
void onApiLightsStatus(AsyncWebServerRequest *request)
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
|
||||
serializeJson(getLedStatusObject()["data"], *response);
|
||||
|
||||
|
@ -906,7 +871,7 @@ void onApiLightsStatus(AsyncWebServerRequest *request)
|
|||
void onApiStopDataSources(AsyncWebServerRequest *request)
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
|
||||
stopPriceNotify();
|
||||
stopBlockNotify();
|
||||
|
@ -917,7 +882,7 @@ void onApiStopDataSources(AsyncWebServerRequest *request)
|
|||
void onApiRestartDataSources(AsyncWebServerRequest *request)
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
|
||||
restartPriceNotify();
|
||||
restartBlockNotify();
|
||||
|
@ -930,7 +895,7 @@ void onApiRestartDataSources(AsyncWebServerRequest *request)
|
|||
void onApiLightsOff(AsyncWebServerRequest *request)
|
||||
{
|
||||
setLights(0, 0, 0);
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onApiLightsSetColor(AsyncWebServerRequest *request)
|
||||
|
@ -938,7 +903,7 @@ void onApiLightsSetColor(AsyncWebServerRequest *request)
|
|||
if (request->hasParam("c"))
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
|
||||
String rgbColor = request->getParam("c")->value();
|
||||
|
||||
|
@ -962,7 +927,7 @@ void onApiLightsSetColor(AsyncWebServerRequest *request)
|
|||
}
|
||||
else
|
||||
{
|
||||
request->send(HTTP_BAD_REQUEST);
|
||||
request->send(400);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -979,7 +944,7 @@ void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
}
|
||||
|
||||
Serial.printf("Invalid values for LED set %d\n", lights.size());
|
||||
request->send(HTTP_BAD_REQUEST);
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -987,27 +952,27 @@ void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
{
|
||||
unsigned int red, green, blue;
|
||||
|
||||
if (lights[i]["red"].is<uint>() && lights[i]["green"].is<uint>() &&
|
||||
lights[i]["blue"].is<uint>())
|
||||
if (lights[i].containsKey("red") && lights[i].containsKey("green") &&
|
||||
lights[i].containsKey("blue"))
|
||||
{
|
||||
red = lights[i]["red"].as<uint>();
|
||||
green = lights[i]["green"].as<uint>();
|
||||
blue = lights[i]["blue"].as<uint>();
|
||||
}
|
||||
else if (lights[i]["hex"].is<const char*>())
|
||||
else if (lights[i].containsKey("hex"))
|
||||
{
|
||||
if (!sscanf(lights[i]["hex"].as<String>().c_str(), "#%02X%02X%02X", &red,
|
||||
&green, &blue) == 3)
|
||||
{
|
||||
Serial.printf("Invalid hex for LED %d\n", i);
|
||||
request->send(HTTP_BAD_REQUEST);
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.printf("No valid color for LED %d\n", i);
|
||||
request->send(HTTP_BAD_REQUEST);
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1018,7 +983,7 @@ void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
pixels.show();
|
||||
saveLedState();
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onIndex(AsyncWebServerRequest *request)
|
||||
|
@ -1028,6 +993,40 @@ void onIndex(AsyncWebServerRequest *request)
|
|||
|
||||
void onNotFound(AsyncWebServerRequest *request)
|
||||
{
|
||||
// Serial.printf("NotFound, URL[%s]\n", request->url());
|
||||
|
||||
// Serial.printf("NotFound, METHOD[%s]\n", request->methodToString());
|
||||
|
||||
// int headers = request->headers();
|
||||
// int i;
|
||||
// for (i = 0; i < headers; i++)
|
||||
// {
|
||||
// AsyncWebHeader *h = request->getHeader(i);
|
||||
// Serial.printf("NotFound HEADER[%s]: %s\n", h->name().c_str(),
|
||||
// h->value().c_str());
|
||||
// }
|
||||
|
||||
// int params = request->params();
|
||||
// for (int i = 0; i < params; i++)
|
||||
// {
|
||||
// const AsyncWebParameter *p = request->getParam(i);
|
||||
// if (p->isFile())
|
||||
// { // p->isPost() is also true
|
||||
// Serial.printf("NotFound FILE[%s]: %s, size: %u\n",
|
||||
// p->name().c_str(), p->value().c_str(), p->size());
|
||||
// }
|
||||
// else if (p->isPost())
|
||||
// {
|
||||
// Serial.printf("NotFound POST[%s]: %s\n", p->name().c_str(),
|
||||
// p->value().c_str());
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Serial.printf("NotFound GET[%s]: %s\n", p->name().c_str(),
|
||||
// p->value().c_str());
|
||||
// }
|
||||
// }
|
||||
|
||||
// Access-Control-Request-Method == POST might be better
|
||||
|
||||
if (request->method() == HTTP_OPTIONS ||
|
||||
|
@ -1035,7 +1034,7 @@ void onNotFound(AsyncWebServerRequest *request)
|
|||
{
|
||||
// Serial.printf("NotFound, Return[%d]\n", 200);
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1068,10 +1067,10 @@ void onApiShowCurrency(AsyncWebServerRequest *request)
|
|||
|
||||
char curChar = getCurrencyChar(currency);
|
||||
|
||||
ScreenHandler::setCurrentCurrency(curChar);
|
||||
ScreenHandler::setCurrentScreen(ScreenHandler::getCurrentScreen());
|
||||
setCurrentCurrency(curChar);
|
||||
setCurrentScreen(getCurrentScreen());
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
request->send(404);
|
||||
|
@ -1082,13 +1081,13 @@ void onApiFrontlightOn(AsyncWebServerRequest *request)
|
|||
{
|
||||
frontlightFadeInAll();
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onApiFrontlightStatus(AsyncWebServerRequest *request)
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
request->beginResponseStream(JSON_CONTENT);
|
||||
request->beginResponseStream("application/json");
|
||||
|
||||
JsonDocument root;
|
||||
|
||||
|
@ -1107,7 +1106,7 @@ void onApiFrontlightFlash(AsyncWebServerRequest *request)
|
|||
{
|
||||
frontlightFlash(preferences.getUInt("flEffectDelay"));
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onApiFrontlightSetBrightness(AsyncWebServerRequest *request)
|
||||
|
@ -1115,11 +1114,11 @@ void onApiFrontlightSetBrightness(AsyncWebServerRequest *request)
|
|||
if (request->hasParam("b"))
|
||||
{
|
||||
frontlightSetBrightness(request->getParam("b")->value().toInt());
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
else
|
||||
{
|
||||
request->send(HTTP_BAD_REQUEST);
|
||||
request->send(400);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1127,31 +1126,6 @@ void onApiFrontlightOff(AsyncWebServerRequest *request)
|
|||
{
|
||||
frontlightFadeOutAll();
|
||||
|
||||
request->send(HTTP_OK);
|
||||
request->send(200);
|
||||
}
|
||||
#endif
|
||||
|
||||
void onApiDNDStatus(AsyncWebServerRequest *request) {
|
||||
JsonDocument doc;
|
||||
doc["enabled"] = dndEnabled;
|
||||
doc["timeBasedEnabled"] = dndTimeBasedEnabled;
|
||||
doc["startTime"] = String(dndTimeRange.startHour) + ":" +
|
||||
(dndTimeRange.startMinute < 10 ? "0" : "") + String(dndTimeRange.startMinute);
|
||||
doc["endTime"] = String(dndTimeRange.endHour) + ":" +
|
||||
(dndTimeRange.endMinute < 10 ? "0" : "") + String(dndTimeRange.endMinute);
|
||||
doc["active"] = isDNDActive();
|
||||
|
||||
String response;
|
||||
serializeJson(doc, response);
|
||||
request->send(200, "application/json", response);
|
||||
}
|
||||
|
||||
void onApiDNDEnable(AsyncWebServerRequest *request) {
|
||||
setDNDEnabled(true);
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void onApiDNDDisable(AsyncWebServerRequest *request) {
|
||||
setDNDEnabled(false);
|
||||
request->send(200);
|
||||
}
|
|
@ -14,7 +14,6 @@
|
|||
#include "lib/price_notify.hpp"
|
||||
#include "lib/screen_handler.hpp"
|
||||
#include "webserver/OneParamRewrite.hpp"
|
||||
#include "lib/mining_pool/pool_factory.hpp"
|
||||
|
||||
extern TaskHandle_t eventSourceTaskHandle;
|
||||
|
||||
|
@ -28,7 +27,8 @@ void onApiStatus(AsyncWebServerRequest *request);
|
|||
void onApiSystemStatus(AsyncWebServerRequest *request);
|
||||
void onApiSetWifiTxPower(AsyncWebServerRequest *request);
|
||||
|
||||
void onApiScreenControl(AsyncWebServerRequest *request);
|
||||
void onApiScreenNext(AsyncWebServerRequest *request);
|
||||
void onApiScreenPrevious(AsyncWebServerRequest *request);
|
||||
|
||||
void onApiShowScreen(AsyncWebServerRequest *request);
|
||||
void onApiShowCurrency(AsyncWebServerRequest *request);
|
||||
|
@ -67,10 +67,6 @@ void eventSourceTask(void *pvParameters);
|
|||
void onApiStopDataSources(AsyncWebServerRequest *request);
|
||||
void onApiRestartDataSources(AsyncWebServerRequest *request);
|
||||
|
||||
void onApiDNDStatus(AsyncWebServerRequest *request);
|
||||
void onApiDNDEnable(AsyncWebServerRequest *request);
|
||||
void onApiDNDDisable(AsyncWebServerRequest *request);
|
||||
|
||||
#ifdef HAS_FRONTLIGHT
|
||||
void onApiFrontlightOn(AsyncWebServerRequest *request);
|
||||
void onApiFrontlightFlash(AsyncWebServerRequest *request);
|
||||
|
|
199
src/main.cpp
199
src/main.cpp
|
@ -22,139 +22,152 @@
|
|||
uint wifiLostConnection;
|
||||
uint priceNotifyLostConnection = 0;
|
||||
uint blockNotifyLostConnection = 0;
|
||||
// char ptrTaskList[1500];
|
||||
|
||||
int64_t getUptime() {
|
||||
return esp_timer_get_time() / 1000000;
|
||||
}
|
||||
extern "C" void app_main()
|
||||
{
|
||||
initArduino();
|
||||
|
||||
void handlePriceNotifyDisconnection() {
|
||||
if (priceNotifyLostConnection == 0) {
|
||||
priceNotifyLostConnection = getUptime();
|
||||
Serial.println(F("Lost price notification connection, trying to reconnect..."));
|
||||
}
|
||||
Serial.begin(115200);
|
||||
setup();
|
||||
|
||||
if ((getUptime() - priceNotifyLostConnection) > 300) { // 5 minutes timeout
|
||||
Serial.println(F("Price notification connection lost for 5 minutes, restarting handler..."));
|
||||
restartPriceNotify();
|
||||
priceNotifyLostConnection = 0;
|
||||
}
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
// vTaskList(ptrTaskList);
|
||||
// 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);
|
||||
|
||||
void handleBlockNotifyDisconnection() {
|
||||
if (blockNotifyLostConnection == 0) {
|
||||
blockNotifyLostConnection = getUptime();
|
||||
Serial.println(F("Lost block notification connection, trying to reconnect..."));
|
||||
}
|
||||
int64_t currentUptime = esp_timer_get_time() / 1000000;
|
||||
;
|
||||
|
||||
if ((getUptime() - blockNotifyLostConnection) > 300) { // 5 minutes timeout
|
||||
Serial.println(F("Block notification connection lost for 5 minutes, restarting handler..."));
|
||||
restartBlockNotify();
|
||||
blockNotifyLostConnection = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void handleFrontlight() {
|
||||
if (!getIsOTAUpdating())
|
||||
{
|
||||
#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) {
|
||||
if (hasLightLevel()) {
|
||||
if (preferences.getUInt("luxLightToggle", DEFAULT_LUX_LIGHT_TOGGLE) != 0)
|
||||
{
|
||||
if (hasLightLevel() && getLightLevel() <= 1 && preferences.getBool("flOffWhenDark", DEFAULT_FL_OFF_WHEN_DARK))
|
||||
{
|
||||
if (frontlightIsOn()) {
|
||||
frontlightFadeOutAll();
|
||||
}
|
||||
}
|
||||
else if (hasLightLevel() && getLightLevel() < preferences.getUInt("luxLightToggle", DEFAULT_LUX_LIGHT_TOGGLE) && !frontlightIsOn())
|
||||
{
|
||||
frontlightFadeInAll();
|
||||
}
|
||||
else if (frontlightIsOn() && getLightLevel() > preferences.getUInt("luxLightToggle", DEFAULT_LUX_LIGHT_TOGGLE))
|
||||
{
|
||||
frontlightFadeOutAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void checkWiFiConnection() {
|
||||
if (!WiFi.isConnected()) {
|
||||
if (!wifiLostConnection) {
|
||||
wifiLostConnection = getUptime();
|
||||
if (!WiFi.isConnected())
|
||||
{
|
||||
if (!wifiLostConnection)
|
||||
{
|
||||
wifiLostConnection = currentUptime;
|
||||
Serial.println(F("Lost WiFi connection, trying to reconnect..."));
|
||||
}
|
||||
if ((getUptime() - wifiLostConnection) > 600) {
|
||||
|
||||
if ((currentUptime - wifiLostConnection) > 600)
|
||||
{
|
||||
Serial.println(F("Still no connection after 10 minutes, restarting..."));
|
||||
delay(2000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
WiFi.begin();
|
||||
} else if (wifiLostConnection) {
|
||||
}
|
||||
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();
|
||||
if (getPriceNotifyInit() && !preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE) && !isPriceNotifyConnected())
|
||||
{
|
||||
priceNotifyLostConnection++;
|
||||
Serial.println(F("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(F("Restarting price handler..."));
|
||||
|
||||
restartPriceNotify();
|
||||
// setupPriceNotify();
|
||||
priceNotifyLostConnection = 0;
|
||||
}
|
||||
setLastBlockUpdate(getUptime());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void monitorDataConnections() {
|
||||
|
||||
// Price notification monitoring
|
||||
if (getPriceNotifyInit() && !preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE) && !isPriceNotifyConnected()) {
|
||||
handlePriceNotifyDisconnection();
|
||||
} else if (priceNotifyLostConnection > 0 && isPriceNotifyConnected()) {
|
||||
else if (priceNotifyLostConnection > 0 && isPriceNotifyConnected())
|
||||
{
|
||||
priceNotifyLostConnection = 0;
|
||||
}
|
||||
|
||||
// Block notification monitoring
|
||||
if (getBlockNotifyInit() && !isBlockNotifyConnected()) {
|
||||
handleBlockNotifyDisconnection();
|
||||
} else if (blockNotifyLostConnection > 0 && isBlockNotifyConnected()) {
|
||||
if (getBlockNotifyInit() && !isBlockNotifyConnected())
|
||||
{
|
||||
blockNotifyLostConnection++;
|
||||
Serial.println(F("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(F("Restarting block handler..."));
|
||||
|
||||
restartBlockNotify();
|
||||
// setupBlockNotify();
|
||||
blockNotifyLostConnection = 0;
|
||||
}
|
||||
}
|
||||
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)) {
|
||||
// if more than 5 price updates are missed, there is probably something wrong, reconnect
|
||||
if ((getLastPriceUpdate(CURRENCY_USD) - currentUptime) > (preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE) * 5))
|
||||
{
|
||||
Serial.println(F("Detected 5 missed price updates... restarting price handler."));
|
||||
|
||||
restartPriceNotify();
|
||||
// setupPriceNotify();
|
||||
|
||||
priceNotifyLostConnection = 0;
|
||||
}
|
||||
|
||||
// Check for missed blocks
|
||||
if ((getLastBlockUpdate() - getUptime()) > 45 * 60) {
|
||||
checkMissedBlocks();
|
||||
// If after 45 minutes no mempool blocks, check the rest API
|
||||
if ((getLastBlockUpdate() - currentUptime) > 45 * 60)
|
||||
{
|
||||
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."));
|
||||
// Mempool source stuck, restart
|
||||
restartBlockNotify();
|
||||
// setupBlockNotify();
|
||||
}
|
||||
// set last block update so it doesn't fetch for 45 minutes
|
||||
setLastBlockUpdate(currentUptime);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void app_main() {
|
||||
initArduino();
|
||||
Serial.begin(115200);
|
||||
setup();
|
||||
|
||||
bool thirdPartySource = getDataSource() == THIRD_PARTY_SOURCE;
|
||||
|
||||
|
||||
while (true) {
|
||||
if (eventSourceTaskHandle != NULL) {
|
||||
xTaskNotifyGive(eventSourceTaskHandle);
|
||||
}
|
||||
|
||||
if (!getIsOTAUpdating()) {
|
||||
handleFrontlight();
|
||||
checkWiFiConnection();
|
||||
|
||||
if (thirdPartySource) {
|
||||
monitorDataConnections();
|
||||
}
|
||||
|
||||
if (getUptime() - getLastTimeSync() > 24 * 60 * 60) {
|
||||
if (currentUptime - getLastTimeSync() > 24 * 60 * 60)
|
||||
{
|
||||
Serial.println(F("Last time update is longer than 24 hours ago, sync again"));
|
||||
syncTime();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
#include <utils.hpp>
|
||||
#include <unity.h>
|
||||
|
||||
void test_parseMiningPoolStatsHashRate1dot34TH(void)
|
||||
{
|
||||
std::string hashrate;
|
||||
std::string label;
|
||||
std::string output;
|
||||
|
||||
parseHashrateString("1340000000000", label, output, 4);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("TH/S", label.c_str(), label.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("1.34", output.c_str(), output.c_str());
|
||||
}
|
||||
|
||||
void test_parseMiningPoolStatsHashRate645GH(void)
|
||||
{
|
||||
std::string hashrate = "645000000000";
|
||||
std::string label;
|
||||
std::string output;
|
||||
|
||||
parseHashrateString(hashrate, label, output, 4);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("GH/S", label.c_str(), label.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("645", output.c_str(), output.c_str());
|
||||
}
|
||||
|
||||
void test_parseMiningPoolStatsHashRateEmpty(void)
|
||||
{
|
||||
std::string hashrate = "";
|
||||
std::string label;
|
||||
std::string output;
|
||||
|
||||
parseHashrateString(hashrate, label, output, 4);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("H/S", label.c_str(), label.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("0", output.c_str(), output.c_str());
|
||||
}
|
||||
|
||||
void test_parseMiningPoolStatsHashRateZero(void)
|
||||
{
|
||||
std::string hashrate = "0";
|
||||
std::string label;
|
||||
std::string output;
|
||||
|
||||
parseHashrateString(hashrate, label, output, 4);
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("H/S", label.c_str(), label.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE("0", output.c_str(), output.c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// not needed when using generate_test_runner.rb
|
||||
int runUnityTests(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_parseMiningPoolStatsHashRate1dot34TH);
|
||||
RUN_TEST(test_parseMiningPoolStatsHashRate645GH);
|
||||
RUN_TEST(test_parseMiningPoolStatsHashRateZero);
|
||||
RUN_TEST(test_parseMiningPoolStatsHashRateEmpty);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return runUnityTests();
|
||||
}
|
||||
|
||||
extern "C" void app_main()
|
||||
{
|
||||
runUnityTests();
|
||||
}
|
Loading…
Reference in a new issue