btclock_v3/src/lib/screen_handler.cpp

377 lines
11 KiB
C++
Raw Normal View History

2023-11-07 00:11:12 +00:00
#include "screen_handler.hpp"
TaskHandle_t priceUpdateTaskHandle;
TaskHandle_t blockUpdateTaskHandle;
2023-11-07 20:26:15 +00:00
TaskHandle_t timeUpdateTaskHandle;
TaskHandle_t taskScreenRotateTaskHandle;
esp_timer_handle_t screenRotateTimer;
esp_timer_handle_t minuteTimer;
2023-11-07 00:11:12 +00:00
std::array<String, NUM_SCREENS> taskEpdContent = {"", "", "", "", "", "", ""};
std::string priceString;
2023-11-07 20:26:15 +00:00
const int usPerSecond = 1000000;
const int usPerMinute = 60 * usPerSecond;
int64_t next_callback_time = 0;
uint currentScreen;
2023-11-07 00:11:12 +00:00
void taskPriceUpdate(void *pvParameters)
{
for (;;)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
unsigned long price = getPrice();
uint firstIndex = 0;
2023-11-07 20:26:15 +00:00
if (getCurrentScreen() == SCREEN_BTC_TICKER)
{
2023-11-07 00:11:12 +00:00
priceString = ("$" + String(price)).c_str();
2023-11-07 20:26:15 +00:00
if (priceString.length() < (NUM_SCREENS))
{
2023-11-07 00:11:12 +00:00
priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' ');
taskEpdContent[0] = "BTC/USD";
firstIndex = 1;
}
2023-11-07 20:26:15 +00:00
}
else if (getCurrentScreen() == SCREEN_MSCW_TIME)
2023-11-07 20:26:15 +00:00
{
2023-11-07 00:11:12 +00:00
priceString = String(int(round(1 / float(price) * 10e7))).c_str();
2023-11-07 20:26:15 +00:00
if (priceString.length() < (NUM_SCREENS))
{
2023-11-07 00:11:12 +00:00
priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' ');
taskEpdContent[0] = "MSCW/TIME";
firstIndex = 1;
}
}
else
2023-11-07 00:11:12 +00:00
{
double supply = getSupplyAtBlock(getBlockHeight());
int64_t marketCap = static_cast<std::int64_t>(supply * double(price));
std::string stringValue = std::to_string(marketCap);
size_t mcLength = stringValue.length();
size_t leadingSpaces = (3 - mcLength % 3) % 3;
stringValue = std::string(leadingSpaces, ' ') + stringValue;
taskEpdContent[0] = "USD/MCAP";
uint groups = (mcLength + leadingSpaces) / 3;
if (groups < NUM_SCREENS) {
firstIndex = 1;
}
for (int i = firstIndex; i < NUM_SCREENS-groups-1; i++) {
taskEpdContent[i] = "";
}
taskEpdContent[NUM_SCREENS-groups-1] = " $ ";
for (uint i = 0; i < groups; i ++)
{
taskEpdContent[(NUM_SCREENS-groups+i)] = stringValue.substr(i*3, 3).c_str();
}
}
if (getCurrentScreen() != SCREEN_MARKET_CAP) {
for (uint i = firstIndex; i < NUM_SCREENS; i++)
{
taskEpdContent[i] = priceString[i];
}
2023-11-07 00:11:12 +00:00
}
setEpdContent(taskEpdContent);
}
}
2023-11-07 20:26:15 +00:00
void taskScreenRotate(void *pvParameters)
{
for (;;)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
int nextScreen = (currentScreen + 1) % 5;
2023-11-08 11:18:59 +00:00
String key = "screen" + String(nextScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true))
{
nextScreen = (nextScreen + 1) % 5;
key = "screen" + String(nextScreen) + "Visible";
}
setCurrentScreen(nextScreen);
2023-11-07 20:26:15 +00:00
}
}
2023-11-07 00:11:12 +00:00
void taskBlockUpdate(void *pvParameters)
{
for (;;)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
std::string blockNrString = String(getBlockHeight()).c_str();
uint firstIndex = 0;
2023-11-07 20:26:15 +00:00
if (getCurrentScreen() != SCREEN_HALVING_COUNTDOWN)
2023-11-07 00:11:12 +00:00
{
2023-11-07 20:26:15 +00:00
if (blockNrString.length() < NUM_SCREENS)
{
blockNrString.insert(blockNrString.begin(), NUM_SCREENS - blockNrString.length(), ' ');
taskEpdContent[0] = "BLOCK/HEIGHT";
firstIndex = 1;
}
for (uint i = firstIndex; i < NUM_SCREENS; i++)
{
taskEpdContent[i] = blockNrString[i];
}
}
else
{
const uint nextHalvingBlock = 210000 - (getBlockHeight() % 210000);
const uint minutesToHalving = nextHalvingBlock * 10;
const int years = floor(minutesToHalving / 525600);
const int days = floor((minutesToHalving - (years * 525600)) / (24 * 60));
const int hours = floor((minutesToHalving - (years * 525600) - (days * (24 * 60))) / 60);
const int mins = floor(minutesToHalving - (years * 525600) - (days * (24 * 60)) - (hours * 60));
taskEpdContent[0] = "BIT/COIN";
taskEpdContent[1] = "HALV/ING";
taskEpdContent[(NUM_SCREENS - 5)] = String(years) + "/YRS";
taskEpdContent[(NUM_SCREENS - 4)] = String(days) + "/DAYS";
taskEpdContent[(NUM_SCREENS - 3)] = String(days) + "/HRS";
taskEpdContent[(NUM_SCREENS - 2)] = String(mins) + "/MINS";
taskEpdContent[(NUM_SCREENS - 1)] = "TO/GO";
2023-11-07 00:11:12 +00:00
}
if (getCurrentScreen() == SCREEN_HALVING_COUNTDOWN || getCurrentScreen() == SCREEN_BLOCK_HEIGHT)
{
2023-11-08 14:33:37 +00:00
setEpdContent(taskEpdContent);
}
2023-11-07 00:11:12 +00:00
}
}
2023-11-07 20:26:15 +00:00
void taskTimeUpdate(void *pvParameters)
{
for (;;)
{
if (getCurrentScreen() == SCREEN_TIME)
{
time_t currentTime;
struct tm timeinfo;
time(&currentTime);
localtime_r(&currentTime, &timeinfo);
std::string timeString;
String minute = String(timeinfo.tm_min);
if (minute.length() < 2)
{
minute = "0" + minute;
}
timeString = std::to_string(timeinfo.tm_hour) + ":" + minute.c_str();
timeString.insert(timeString.begin(), NUM_SCREENS - timeString.length(), ' ');
taskEpdContent[0] = String(timeinfo.tm_mday) + "/" + String(timeinfo.tm_mon + 1);
for (uint i = 1; i < NUM_SCREENS; i++)
{
taskEpdContent[i] = timeString[i];
}
setEpdContent(taskEpdContent);
}
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
}
}
void IRAM_ATTR minuteTimerISR(void *arg)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(timeUpdateTaskHandle, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE)
{
portYIELD_FROM_ISR();
}
int64_t current_time = esp_timer_get_time();
next_callback_time = current_time + usPerMinute;
}
void IRAM_ATTR screenRotateTimerISR(void *arg)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(taskScreenRotateTaskHandle, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE)
{
portYIELD_FROM_ISR();
}
}
2023-11-07 00:11:12 +00:00
void setupTasks()
{
xTaskCreate(taskPriceUpdate, "updatePrice", 3072, NULL, tskIDLE_PRIORITY, &priceUpdateTaskHandle);
2023-11-07 20:26:15 +00:00
xTaskCreate(taskBlockUpdate, "updateBlock", 2048, NULL, tskIDLE_PRIORITY, &blockUpdateTaskHandle);
xTaskCreate(taskTimeUpdate, "updateTime", 4096, NULL, tskIDLE_PRIORITY, &timeUpdateTaskHandle);
xTaskCreate(taskScreenRotate, "rotateScreen", 2048, NULL, tskIDLE_PRIORITY, &taskScreenRotateTaskHandle);
setCurrentScreen(preferences.getUInt("currentScreen", 0));
2023-11-07 20:26:15 +00:00
}
void setupTimeUpdateTimer(void *pvParameters)
{
const esp_timer_create_args_t minuteTimerConfig = {
.callback = &minuteTimerISR,
.name = "minute_timer"};
esp_timer_create(&minuteTimerConfig, &minuteTimer);
time_t currentTime;
struct tm timeinfo;
time(&currentTime);
localtime_r(&currentTime, &timeinfo);
uint32_t secondsUntilNextMinute = 60 - timeinfo.tm_sec;
if (secondsUntilNextMinute > 0)
vTaskDelay(pdMS_TO_TICKS((secondsUntilNextMinute * 1000)));
esp_timer_start_periodic(minuteTimer, usPerMinute);
xTaskNotifyGive(timeUpdateTaskHandle);
vTaskDelete(NULL);
}
void setupScreenRotateTimer(void *pvParameters)
{
const esp_timer_create_args_t screenRotateTimerConfig = {
.callback = &screenRotateTimerISR,
.name = "screen_rotate_timer"};
esp_timer_create(&screenRotateTimerConfig, &screenRotateTimer);
if (preferences.getBool("timerActive", true))
{
esp_timer_start_periodic(screenRotateTimer, getTimerSeconds() * usPerSecond);
}
2023-11-07 20:26:15 +00:00
vTaskDelete(NULL);
}
uint getTimerSeconds()
{
return preferences.getUInt("timerSeconds", 1800);
}
bool isTimerActive()
{
return esp_timer_is_active(screenRotateTimer);
}
void setTimerActive(bool status)
{
if (status)
{
esp_timer_start_periodic(screenRotateTimer, getTimerSeconds() * usPerSecond);
2023-11-08 11:18:59 +00:00
queueLedEffect(LED_EFFECT_START_TIMER);
preferences.putBool("timerActive", true);
2023-11-07 20:26:15 +00:00
}
else
{
esp_timer_stop(screenRotateTimer);
2023-11-08 11:18:59 +00:00
queueLedEffect(LED_EFFECT_PAUSE_TIMER);
preferences.putBool("timerActive", false);
2023-11-07 20:26:15 +00:00
}
if (eventSourceTaskHandle != NULL)
xTaskNotifyGive(eventSourceTaskHandle);
2023-11-07 20:26:15 +00:00
}
void toggleTimerActive()
{
2023-11-08 11:18:59 +00:00
setTimerActive(!isTimerActive());
}
2023-11-07 20:26:15 +00:00
uint getCurrentScreen()
{
return currentScreen;
}
void setCurrentScreen(uint newScreen)
{
if (newScreen != SCREEN_CUSTOM)
{
preferences.putUInt("currentScreen", newScreen);
}
currentScreen = newScreen;
switch (currentScreen)
{
case SCREEN_TIME:
xTaskNotifyGive(timeUpdateTaskHandle);
break;
case SCREEN_HALVING_COUNTDOWN:
case SCREEN_BLOCK_HEIGHT:
xTaskNotifyGive(blockUpdateTaskHandle);
break;
case SCREEN_MARKET_CAP:
2023-11-07 20:26:15 +00:00
case SCREEN_MSCW_TIME:
case SCREEN_BTC_TICKER:
xTaskNotifyGive(priceUpdateTaskHandle);
break;
}
if (eventSourceTaskHandle != NULL)
xTaskNotifyGive(eventSourceTaskHandle);
2023-11-08 11:18:59 +00:00
}
void nextScreen()
{
int newCurrentScreen = (getCurrentScreen() + 1) % SCREEN_COUNT;
String key = "screen" + String(newCurrentScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true))
{
newCurrentScreen = (newCurrentScreen + 1) % SCREEN_COUNT;
key = "screen" + String(newCurrentScreen) + "Visible";
}
setCurrentScreen(newCurrentScreen);
}
void previousScreen()
{
2023-11-08 11:18:59 +00:00
int newCurrentScreen = modulo(getCurrentScreen() - 1, SCREEN_COUNT);
String key = "screen" + String(newCurrentScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true))
{
newCurrentScreen = modulo(newCurrentScreen - 1, SCREEN_COUNT);
key = "screen" + String(newCurrentScreen) + "Visible";
}
setCurrentScreen(newCurrentScreen);
}
void showSystemStatusScreen()
{
std::array<String, NUM_SCREENS> sysStatusEpdContent = {"", "", "", "", "", "", ""};
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";
2023-11-08 11:18:59 +00:00
sysStatusEpdContent[NUM_SCREENS - 1] = String((int)round(ESP.getFreeHeap() / 1024)) + "/" + (int)round(ESP.getHeapSize() / 1024);
2023-11-08 11:18:59 +00:00
setCurrentScreen(SCREEN_CUSTOM);
setEpdContent(sysStatusEpdContent);
2023-11-07 00:11:12 +00:00
}