btclock_v2/src/lib/functions.cpp

465 lines
12 KiB
C++

#include "functions.hpp"
Preferences preferences;
uint timerSeconds;
uint currentScreen;
std::map<int, std::string> screenNameMap;
#ifndef NO_MCP
Adafruit_MCP23X17 mcp;
const int MCP_INT_PIN = 8;
#endif
bool timerRunning = true;
uint wifiConnectionLostCount = 0;
#ifdef WITH_RGB_LED
#ifndef NEOPIXEL_PIN
#define NEOPIXEL_PIN 34
#endif
#ifndef NEOPIXEL_COUNT
#define NEOPIXEL_COUNT 3
#endif
Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
#endif
String softAP_SSID;
String softAP_password;
WiFiMulti wifiMulti;
WiFiManager wm;
bool screenVisible[5];
void setupSoftAP()
{
byte mac[6];
WiFi.macAddress(mac);
softAP_SSID = String("BTClock" + String(mac[5], 16) + String(mac[6], 16));
WiFi.setHostname(softAP_SSID.c_str());
softAP_password = base64::encode(String(mac[2], 16) + String(mac[4], 16) + String(mac[5], 16) + String(mac[6], 16)).substring(2, 10);
}
void setupComponents()
{
if (psramInit())
{
Serial.println("\nPSRAM is correctly initialized");
}
else
{
Serial.println("PSRAM not available");
}
#ifdef WITH_RGB_LED
pixels.begin();
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
pixels.setPixelColor(1, pixels.Color(0, 255, 0));
pixels.setPixelColor(2, pixels.Color(0, 0, 255));
pixels.setPixelColor(3, pixels.Color(255, 255, 255));
pixels.show();
#endif
// delay(3000);
// Serial.println("Leds should be on");
#ifndef NO_MCP
if (!mcp.begin_I2C())
{
Serial.println("Error MCP23017");
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
pixels.setPixelColor(1, pixels.Color(255, 0, 0));
pixels.setPixelColor(2, pixels.Color(255, 0, 0));
pixels.setPixelColor(3, pixels.Color(255, 0, 0));
pixels.show();
while (1)
;
}
else
{
Serial.println("MCP23017 ok");
pixels.setPixelColor(0, pixels.Color(0, 255, 0));
pixels.setPixelColor(1, pixels.Color(0, 255, 0));
pixels.setPixelColor(2, pixels.Color(0, 255, 0));
pixels.setPixelColor(3, pixels.Color(0, 255, 0));
pixels.show();
// delay(200);
pinMode(MCP_INT_PIN, INPUT);
mcp.setupInterrupts(true, false, LOW);
}
#endif
#ifdef WITH_BUTTONS
for (int i = 0; i < 4; i++)
{
mcp.pinMode(i, INPUT_PULLUP);
mcp.setupInterruptPin(i, LOW);
}
#endif
}
void synchronizeTime()
{
configTime(preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS), 0, NTP_SERVER);
struct tm timeinfo;
while (!getLocalTime(&timeinfo))
{
configTime(preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS), 0, NTP_SERVER);
delay(500);
Serial.println("Retry set time");
}
rtc.setTimeStruct(timeinfo);
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}
void setupWifi()
{
#ifndef NO_MCP
if (mcp.digitalRead(3) == LOW)
{
pixels.setPixelColor(0, pixels.Color(0, 0, 255));
pixels.setPixelColor(1, pixels.Color(0, 0, 255));
pixels.setPixelColor(2, pixels.Color(0, 0, 255));
pixels.setPixelColor(3, pixels.Color(0, 0, 255));
pixels.show();
delay(1500);
if (mcp.digitalRead(3) == LOW)
{
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
pixels.setPixelColor(1, pixels.Color(0, 0, 255));
pixels.setPixelColor(2, pixels.Color(255, 0, 0));
pixels.setPixelColor(3, pixels.Color(0, 0, 255));
pixels.show();
Serial.println("Erasing WiFi Config, restarting");
wm.resetSettings();
ESP.restart();
}
return;
}
#endif
setupSoftAP();
wm.setConfigPortalTimeout(preferences.getUInt("wpTimeout", 600));
wm.setWiFiAutoReconnect(true);
wm.setAPCallback([&](WiFiManager *wifiManager)
{
showSetupQr(softAP_SSID, softAP_password);
Serial.printf("Entered config mode:ip=%s, ssid='%s', pass='%s'\n",
WiFi.softAPIP().toString().c_str(),
wifiManager->getConfigPortalSSID().c_str(),
softAP_password.c_str()); });
bool ac = wm.autoConnect(softAP_SSID.c_str(), softAP_password.c_str());
}
void setupPreferences()
{
preferences.begin("btclock", false);
timerSeconds = preferences.getUInt("timerSeconds", 1800);
currentScreen = preferences.getUInt("currentScreen", 0);
// handleScreenTasks(currentScreen);
setFgColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR));
setBgColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR));
preferences.getBool("ledFlashOnUpd", false);
screenNameMap = {{SCREEN_BLOCK_HEIGHT, "Block Height"},
{SCREEN_MSCW_TIME, "Sats per dollar"},
{SCREEN_BTC_TICKER, "Ticker"},
{SCREEN_TIME, "Time"},
{SCREEN_HALVING_COUNTDOWN, "Halving countdown"}};
#ifdef WITH_RGB_LED
pixels.setBrightness(preferences.getUInt("ledBrightness", 128));
pixels.setPixelColor(3, pixels.Color(255, 0, 0));
pixels.setPixelColor(2, pixels.Color(0, 255, 0));
pixels.setPixelColor(1, pixels.Color(0, 0, 255));
pixels.setPixelColor(0, pixels.Color(255, 255, 255));
pixels.show();
#endif
for (int i = 0; i < screenNameMap.size(); i++)
{
String key = "screen" + String(i) + "Visible";
screenVisible[i] = preferences.getBool(key.c_str(), true); // Default to true if not set
}
xTaskCreate(timebasedChangeTask, "tbc", 5000, NULL, 15, NULL);
}
uint getCurrentScreen()
{
return currentScreen;
}
void setCurrentScreen(uint screen)
{
if (screen != SCREEN_CUSTOM)
{
preferences.putUInt("currentScreen", screen);
}
currentScreen = screen;
handleScreenTasks(screen);
}
void handleScreenTasks(uint screen)
{
if (blockNotifyTaskHandle)
vTaskSuspend(blockNotifyTaskHandle);
if (getPriceTaskHandle)
vTaskSuspend(getPriceTaskHandle);
if (minuteTaskHandle)
vTaskSuspend(minuteTaskHandle);
switch (currentScreen)
{
case SCREEN_BLOCK_HEIGHT:
if (blockNotifyTaskHandle)
{
vTaskResume(blockNotifyTaskHandle);
}
break;
case SCREEN_HALVING_COUNTDOWN:
if (blockNotifyTaskHandle)
vTaskResume(blockNotifyTaskHandle);
break;
case SCREEN_BTC_TICKER:
if (getPriceTaskHandle)
vTaskResume(getPriceTaskHandle);
break;
case SCREEN_MSCW_TIME:
if (getPriceTaskHandle)
vTaskResume(getPriceTaskHandle);
break;
case SCREEN_TIME:
if (minuteTaskHandle)
{
TimeScreen::onActivate();
vTaskResume(minuteTaskHandle);
}
break;
}
}
void toggleScreenTimer()
{
timerRunning = !timerRunning;
if (!timerRunning)
{
Serial.println("Stopping screen timer...");
for (int i = NEOPIXEL_COUNT; i >= 0; i--)
{
for (int j = NEOPIXEL_COUNT; j >= 0; j--)
{
uint32_t c = pixels.Color(0, 0, 0);
if (i == j)
c = pixels.Color(0, 255, 0);
pixels.setPixelColor(j, c);
}
pixels.show();
delay(100);
}
delay(900);
pixels.clear();
pixels.show();
}
else
{
Serial.println("Starting screen timer...");
pixels.setPixelColor(3, pixels.Color(0, 255, 0));
pixels.setPixelColor(2, pixels.Color(0, 0, 0));
pixels.setPixelColor(1, pixels.Color(0, 0, 0));
pixels.setPixelColor(0, pixels.Color(0, 0, 0));
pixels.show();
delay(1000);
for (int i = NEOPIXEL_COUNT; i--; i > 0)
{
for (int j = NEOPIXEL_COUNT; j--; j > 0)
{
uint32_t c = pixels.Color(0, 0, 0);
if (i == j)
c = pixels.Color(0, 255, 0);
pixels.setPixelColor(j, c);
}
pixels.show();
delay(100);
}
pixels.clear();
pixels.show();
}
}
void timebasedChangeTask(void *parameter)
{
uint32_t moment = millis();
for (;;)
{
if (millis() - moment > timerSeconds * 1000 && timerRunning)
{
int newCurrentScreen = (getCurrentScreen() + 1) % screenCount;
String key = "screen" + String(newCurrentScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true))
{
newCurrentScreen = (newCurrentScreen + 1) % screenCount;
key = "screen" + String(newCurrentScreen) + "Visible";
}
setCurrentScreen(newCurrentScreen);
moment = millis();
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
int modulo(int x, int N)
{
return (x % N + N) % N;
}
void nextScreen()
{
int newCurrentScreen = (getCurrentScreen() + 1) % screenCount;
String key = "screen" + String(newCurrentScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true))
{
newCurrentScreen = (newCurrentScreen + 1) % screenCount;
key = "screen" + String(newCurrentScreen) + "Visible";
}
setCurrentScreen(newCurrentScreen);
}
void previousScreen()
{
int newCurrentScreen = modulo(getCurrentScreen() - 1, screenCount);
String key = "screen" + String(newCurrentScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true))
{
newCurrentScreen = modulo(newCurrentScreen - 1, screenCount);
key = "screen" + String(newCurrentScreen) + "Visible";
}
setCurrentScreen(newCurrentScreen);
}
void showNetworkSettings()
{
std::array<String, 7> epdContent = {"", "", "", "", "", "", ""};
String ipAddr = WiFi.localIP().toString();
String subNet = WiFi.subnetMask().toString();
epdContent[1] = "IP/Subnet";
int ipAddrPos = 0;
int subnetPos = 0;
for (int i = 0; i < 4; i++)
{
epdContent[2 + 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);
}
CustomTextScreen::setText(epdContent);
setCurrentScreen(SCREEN_CUSTOM);
}
void setLights(int r, int g, int b)
{
#ifdef WITH_RGB_LED
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
pixels.setPixelColor(i, pixels.Color(r, g, b));
}
pixels.show();
#endif
}
void flashTemporaryLights(int r, int g, int b)
{
#ifdef WITH_RGB_LED
uint32_t oldLights[NEOPIXEL_COUNT];
// get current state
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
oldLights[i] = pixels.getPixelColor(i);
}
// flash three times in given color
for (int t = 0; t < 3; t++)
{
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
pixels.setPixelColor(i, pixels.Color(r, g, b));
}
pixels.show();
delay(200);
pixels.clear();
pixels.show();
delay(200);
}
// revert to previous state
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
pixels.setPixelColor(i, oldLights[i]);
}
pixels.show();
#endif
}
void setupI2C()
{
bool slaveMode = preferences.getBool("I2CSlaveMode", false);
if (slaveMode)
{
Serial.println("I2C Slave Mode enabled");
Wire.onReceive(onI2CReceive);
Wire.begin((uint8_t)I2C_DEV_ADDR);
}
}
void onI2CReceive(int len)
{
Serial.printf("onReceive[%d]: ", len);
while (Wire.available())
{
Serial.write(Wire.read());
}
Serial.println();
}
void onI2CRequest()
{
Wire.print("I2C Packets.");
Serial.println("onRequest");
}