Add Mow mode notation and setting
Some checks failed
BTClock CI / build (push) Failing after 30s
BTClock CI / merge (map[name:btclock_rev_b version:esp32s3], 213epd) (push) Has been skipped
BTClock CI / merge (map[name:btclock_v8 version:esp32s3], 213epd) (push) Has been skipped
BTClock CI / merge (map[name:lolin_s3_mini version:esp32s3], 213epd) (push) Has been skipped
BTClock CI / merge (map[name:lolin_s3_mini version:esp32s3], 29epd) (push) Has been skipped
BTClock CI / release (push) Has been skipped

This commit is contained in:
Djuri Baars 2024-11-27 11:33:12 +01:00
parent 239297c26d
commit d37307cccf
10 changed files with 124 additions and 15 deletions

2
data

@ -1 +1 @@
Subproject commit 5066032a55b5436ec5c888cf0eb91bdc9e71bea0 Subproject commit d74e9dab60772ef11f9b033cfe982d216a9c95ee

View file

@ -67,13 +67,13 @@ char getCurrencyChar(const std::string& input)
return CURRENCY_USD; // Assuming USD is the default for unknown inputs return CURRENCY_USD; // Assuming USD is the default for unknown inputs
} }
std::array<std::string, NUM_SCREENS> parsePriceData(std::uint32_t price, char currencySymbol, bool useSuffixFormat) std::array<std::string, NUM_SCREENS> parsePriceData(std::uint32_t price, char currencySymbol, bool useSuffixFormat, bool mowMode)
{ {
std::array<std::string, NUM_SCREENS> ret; std::array<std::string, NUM_SCREENS> ret;
std::string priceString; std::string priceString;
if (std::to_string(price).length() >= NUM_SCREENS || useSuffixFormat) if (std::to_string(price).length() >= NUM_SCREENS || useSuffixFormat)
{ {
priceString = getCurrencySymbol(currencySymbol) + formatNumberWithSuffix(price, NUM_SCREENS - 2); priceString = getCurrencySymbol(currencySymbol) + formatNumberWithSuffix(price, NUM_SCREENS - 2, mowMode);
} }
else else
{ {

View file

@ -19,7 +19,7 @@ const std::string CURRENCY_CODE_JPY = "JPY";
const std::string CURRENCY_CODE_AUD = "AUD"; const std::string CURRENCY_CODE_AUD = "AUD";
const std::string CURRENCY_CODE_CAD = "CAD"; const std::string CURRENCY_CODE_CAD = "CAD";
std::array<std::string, NUM_SCREENS> parsePriceData(std::uint32_t price, char currency, bool useSuffixFormat = false); std::array<std::string, NUM_SCREENS> parsePriceData(std::uint32_t price, char currency, bool useSuffixFormat = false, bool mowMode = false);
std::array<std::string, NUM_SCREENS> parseSatsPerCurrency(std::uint32_t price, char currencySymbol, bool withSatsSymbol); std::array<std::string, NUM_SCREENS> parseSatsPerCurrency(std::uint32_t price, char currencySymbol, bool withSatsSymbol);
std::array<std::string, NUM_SCREENS> parseBlockHeight(std::uint32_t blockHeight); std::array<std::string, NUM_SCREENS> parseBlockHeight(std::uint32_t blockHeight);
std::array<std::string, NUM_SCREENS> parseHalvingCountdown(std::uint32_t blockHeight, bool asBlocks); std::array<std::string, NUM_SCREENS> parseHalvingCountdown(std::uint32_t blockHeight, bool asBlocks);

View file

@ -28,7 +28,12 @@ double getSupplyAtBlock(std::uint32_t blockNr)
return totalBitcoinInCirculation; return totalBitcoinInCirculation;
} }
std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters) std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters)
{
return formatNumberWithSuffix(num, numCharacters, false);
}
std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters, bool mowMode)
{ {
static char result[20]; // Adjust size as needed static char result[20]; // Adjust size as needed
const long long quadrillion = 1000000000000000LL; const long long quadrillion = 1000000000000000LL;
@ -56,30 +61,36 @@ std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters)
numDouble /= billion; numDouble /= billion;
suffix = 'B'; suffix = 'B';
} }
else if (num >= million || numDigits > 6) else if (num >= million || numDigits > 6 || (mowMode && num >= thousand))
{ {
numDouble /= million; numDouble /= million;
suffix = 'M'; suffix = 'M';
} }
else if (num >= thousand || numDigits > 3) else if (!mowMode && (num >= thousand || numDigits > 3))
{ {
numDouble /= thousand; numDouble /= thousand;
suffix = 'K'; suffix = 'K';
} }
else else if (!mowMode)
{ {
snprintf(result, sizeof(result), "%llu", (unsigned long long)num); snprintf(result, sizeof(result), "%llu", (unsigned long long)num);
// sprintf(result, "%llu", (unsigned long long)num);
return result; return result;
} }
else // mowMode is true and num < 1000
{
numDouble /= million;
suffix = 'M';
}
// Add suffix // Add suffix
int len = snprintf(result, sizeof(result), "%.0f%c", numDouble, suffix); int len = snprintf(result, sizeof(result), "%.0f%c", numDouble, suffix);
// If there's room, add decimal places // If there's room, add more decimal places
if (len < numCharacters) if (len < numCharacters)
{ {
snprintf(result, sizeof(result), "%.*f%c", numCharacters - len - 1, numDouble, suffix); int restLen = mowMode ? numCharacters - len : numCharacters - len - 1;
snprintf(result, sizeof(result), "%.*f%c", restLen, numDouble, suffix);
} }
return result; return result;

View file

@ -11,4 +11,5 @@ int modulo(int x,int N);
double getSupplyAtBlock(std::uint32_t blockNr); double getSupplyAtBlock(std::uint32_t blockNr);
std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters = 4); std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters = 4);
std::string formatNumberWithSuffix(std::uint64_t num, int numCharacters, bool mowMode);
int64_t getAmountInSatoshis(std::string bolt11); int64_t getAmountInSatoshis(std::string bolt11);

View file

@ -18,6 +18,8 @@
#define DEFAULT_DISABLE_FL false #define DEFAULT_DISABLE_FL false
#define DEFAULT_OWN_DATA_SOURCE true #define DEFAULT_OWN_DATA_SOURCE true
#define DEFAULT_STAGING_SOURCE false #define DEFAULT_STAGING_SOURCE false
#define DEFAULT_MOW_MODE false
#define DEFAULT_V2_SOURCE_CURRENCY CURRENCY_USD #define DEFAULT_V2_SOURCE_CURRENCY CURRENCY_USD

View file

@ -283,7 +283,10 @@ void prepareDisplayUpdateTask(void *pvParameters)
} }
else else
{ {
if (epdContent[epdIndex].length() > 1 && epdContent[epdIndex].indexOf(".") == -1) if (epdContent[epdIndex].length() == 2) {
showChars(epdIndex, epdContent[epdIndex], updatePartial, &FONT_BIG);
}
else if (epdContent[epdIndex].length() > 1 && epdContent[epdIndex].indexOf(".") == -1)
{ {
if (epdContent[epdIndex].equals("STS")) if (epdContent[epdIndex].equals("STS"))
{ {
@ -407,6 +410,32 @@ void splitText(const uint dispNum, const String &top, const String &bottom,
displays[dispNum].print(bottom); displays[dispNum].print(bottom);
} }
// void showChars(const uint dispNum, const String &chars, bool partial,
// const GFXfont *font)
// {
// displays[dispNum].setRotation(2);
// displays[dispNum].setFont(font);
// displays[dispNum].setTextColor(getFgColor());
// int16_t tbx, tby;
// uint16_t tbw, tbh;
// displays[dispNum].getTextBounds(chars, 0, 0, &tbx, &tby, &tbw, &tbh);
// // center the bounding box by transposition of the origin:
// uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
// uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
// displays[dispNum].fillScreen(getBgColor());
// displays[dispNum].setCursor(x, y);
// displays[dispNum].print(chars);
// // 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, void showDigit(const uint dispNum, char chr, bool partial,
const GFXfont *font) const GFXfont *font)
{ {
@ -466,6 +495,18 @@ void showDigit(const uint dispNum, char chr, bool partial,
// displays[dispNum].println("Y = " + y); // displays[dispNum].println("Y = " + y);
} }
int16_t calculateDescent(const GFXfont *font) {
int16_t maxDescent = 0;
for (uint16_t i = font->first; i <= font->last; i++) {
GFXglyph *glyph = &font->glyph[i - font->first];
int16_t descent = glyph->yOffset;
if (descent > maxDescent) {
maxDescent = descent;
}
}
return maxDescent;
}
void showChars(const uint dispNum, const String &chars, bool partial, void showChars(const uint dispNum, const String &chars, bool partial,
const GFXfont *font) const GFXfont *font)
{ {
@ -475,12 +516,35 @@ void showChars(const uint dispNum, const String &chars, bool partial,
int16_t tbx, tby; int16_t tbx, tby;
uint16_t tbw, tbh; uint16_t tbw, tbh;
displays[dispNum].getTextBounds(chars, 0, 0, &tbx, &tby, &tbw, &tbh); displays[dispNum].getTextBounds(chars, 0, 0, &tbx, &tby, &tbw, &tbh);
int16_t descent = calculateDescent(font);
// center the bounding box by transposition of the origin: // center the bounding box by transposition of the origin:
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx; uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby; uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
displays[dispNum].fillScreen(getBgColor()); displays[dispNum].fillScreen(getBgColor());
displays[dispNum].setCursor(x, y); // displays[dispNum].setCursor(x, y);
displays[dispNum].print(chars); // displays[dispNum].print(chars);
for (int i = 0; i < chars.length(); i++) {
char c = chars[i];
if (c == '.' || c == ',') {
// For the dot, calculate its specific descent
GFXglyph *dotGlyph = &font->glyph[c -font->first];
int16_t dotDescent = dotGlyph->yOffset;
// Draw the dot with adjusted y-position
displays[dispNum].setCursor(x, y + dotDescent + dotGlyph->height);
displays[dispNum].print(c);
} else {
// For other characters, use the original y-position
displays[dispNum].setCursor(x, y);
displays[dispNum].print(c);
}
// Move x-position for the next character
x += font->glyph[c - font->first].xAdvance;
}
} }
int getBgColor() { return bgColor; } int getBgColor() { return bgColor; }

View file

@ -41,7 +41,7 @@ void workerTask(void *pvParameters) {
uint price = getPrice(currency); uint price = getPrice(currency);
if (getCurrentScreen() == SCREEN_BTC_TICKER) { if (getCurrentScreen() == SCREEN_BTC_TICKER) {
taskEpdContent = parsePriceData(price, currency, preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE)); taskEpdContent = parsePriceData(price, currency, preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE), preferences.getBool("mowMode", DEFAULT_MOW_MODE));
} else if (getCurrentScreen() == SCREEN_SATS_PER_CURRENCY) { } else if (getCurrentScreen() == SCREEN_SATS_PER_CURRENCY) {
taskEpdContent = parseSatsPerCurrency(price, currency, preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL)); taskEpdContent = parseSatsPerCurrency(price, currency, preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL));
} else { } else {

View file

@ -546,6 +546,7 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
"mdnsEnabled", "otaEnabled", "stealFocus", "mdnsEnabled", "otaEnabled", "stealFocus",
"mcapBigChar", "useSatsSymbol", "useBlkCountdown", "mcapBigChar", "useSatsSymbol", "useBlkCountdown",
"suffixPrice", "disableLeds", "ownDataSource", "suffixPrice", "disableLeds", "ownDataSource",
"mowMode",
"flAlwaysOn", "flDisable", "flFlashOnUpd", "flAlwaysOn", "flDisable", "flFlashOnUpd",
"mempoolSecure", "useNostr", "bitaxeEnabled", "mempoolSecure", "useNostr", "bitaxeEnabled",
"nostrZapNotify", "stagingSource", "httpAuthEnabled"}; "nostrZapNotify", "stagingSource", "httpAuthEnabled"};
@ -687,6 +688,7 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
root["useBlkCountdown"] = preferences.getBool("useBlkCountdown", DEFAULT_USE_BLOCK_COUNTDOWN); root["useBlkCountdown"] = preferences.getBool("useBlkCountdown", DEFAULT_USE_BLOCK_COUNTDOWN);
root["suffixPrice"] = preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE); root["suffixPrice"] = preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE);
root["disableLeds"] = preferences.getBool("disableLeds", DEFAULT_DISABLE_LEDS); root["disableLeds"] = preferences.getBool("disableLeds", DEFAULT_DISABLE_LEDS);
root["mowMode"] = preferences.getBool("mowMode", DEFAULT_MOW_MODE);
root["hostnamePrefix"] = preferences.getString("hostnamePrefix", DEFAULT_HOSTNAME_PREFIX); root["hostnamePrefix"] = preferences.getString("hostnamePrefix", DEFAULT_HOSTNAME_PREFIX);
root["hostname"] = getMyHostname(); root["hostname"] = getMyHostname();

View file

@ -86,6 +86,33 @@ void test_PriceOf1MillionUsd(void)
TEST_ASSERT_EQUAL_STRING("M", output[NUM_SCREENS - 1].c_str()); TEST_ASSERT_EQUAL_STRING("M", output[NUM_SCREENS - 1].c_str());
} }
void test_PriceSuffixMode(void)
{
std::array<std::string, NUM_SCREENS> output = parsePriceData(93000, '$', true, false);
TEST_ASSERT_EQUAL_STRING("BTC/USD", output[0].c_str());
TEST_ASSERT_EQUAL_STRING("9", output[NUM_SCREENS - 5].c_str());
TEST_ASSERT_EQUAL_STRING("3", output[NUM_SCREENS - 4].c_str());
TEST_ASSERT_EQUAL_STRING(".", output[NUM_SCREENS - 3].c_str());
TEST_ASSERT_EQUAL_STRING("0", output[NUM_SCREENS - 2].c_str());
TEST_ASSERT_EQUAL_STRING("K", output[NUM_SCREENS - 1].c_str());
}
void test_PriceSuffixModeMow(void)
{
std::array<std::string, NUM_SCREENS> output = parsePriceData(93000, '$', true, true);
std::string joined = joinArrayWithBrackets(output);
TEST_ASSERT_EQUAL_STRING_MESSAGE("$", output[0].c_str(), joined.c_str());
TEST_ASSERT_EQUAL_STRING_MESSAGE(".", output[NUM_SCREENS - 5].c_str(), joined.c_str());
TEST_ASSERT_EQUAL_STRING_MESSAGE("0", output[NUM_SCREENS - 4].c_str(), joined.c_str());
TEST_ASSERT_EQUAL_STRING_MESSAGE("9", output[NUM_SCREENS - 3].c_str(), joined.c_str());
TEST_ASSERT_EQUAL_STRING_MESSAGE("3", output[NUM_SCREENS - 2].c_str(), joined.c_str());
TEST_ASSERT_EQUAL_STRING_MESSAGE("M", output[NUM_SCREENS - 1].c_str(), joined.c_str());
}
void test_McapLowerUsd(void) void test_McapLowerUsd(void)
{ {
std::array<std::string, NUM_SCREENS> output = parseMarketCap(810000, 26000, '$', true); std::array<std::string, NUM_SCREENS> output = parseMarketCap(810000, 26000, '$', true);
@ -203,6 +230,8 @@ int runUnityTests(void)
RUN_TEST(test_Mcap1TrillionEurSmallChars); RUN_TEST(test_Mcap1TrillionEurSmallChars);
RUN_TEST(test_Mcap1TrillionJpy); RUN_TEST(test_Mcap1TrillionJpy);
RUN_TEST(test_Mcap1TrillionJpySmallChars); RUN_TEST(test_Mcap1TrillionJpySmallChars);
RUN_TEST(test_PriceSuffixMode);
RUN_TEST(test_PriceSuffixModeMow);
return UNITY_END(); return UNITY_END();
} }