465 lines
12 KiB
C++
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");
|
|
}
|