More memory optimizations

This commit is contained in:
Djuri Baars 2023-10-30 00:48:08 +01:00
parent e1648a9a42
commit 1f7946c30e
26 changed files with 403 additions and 286 deletions

View File

@ -1,5 +1,8 @@
#include "epd.hpp"
int fgColor; int fgColor;
int bgColor; int bgColor;
uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX];
int getBgColor() int getBgColor()
{ {
@ -20,3 +23,83 @@ void setFgColor(int color)
{ {
fgColor = color; fgColor = color;
} }
void showSetupQr(const String &ssid, const String &password)
{
char displayIndex = 6;
const String text = "WIFI:S:" + ssid + ";T:WPA;P:" + password + ";;";
uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX];
bool ok = qrcodegen_encodeText(text.c_str(), tempBuffer, qrcode, qrcodegen_Ecc_LOW,
qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true);
const int size = qrcodegen_getSize(qrcode);
const int padding = floor(float(displays[displayIndex].width() - (size * 4)) / 2);
const int paddingY = floor(float(displays[displayIndex].height() - (size * 4)) / 2);
displays[displayIndex].setPartialWindow(0, 0, displays[displayIndex].width(), displays[displayIndex].height());
displays[displayIndex].fillScreen(GxEPD_WHITE);
const int border = 0;
for (int y = -border; y < size * 4 + border; y++)
{
for (int x = -border; x < size * 4 + border; x++)
{
displays[displayIndex].drawPixel(padding + x, paddingY + y, qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4)) ? GxEPD_BLACK : GxEPD_WHITE);
}
}
displays[displayIndex].display(true);
displayIndex = 4;
displays[displayIndex].setPartialWindow(0, 0, displays[displayIndex].width(), displays[displayIndex].height());
displays[displayIndex].fillScreen(GxEPD_WHITE);
displays[displayIndex].setTextColor(GxEPD_BLACK);
displays[displayIndex].setCursor(0, 50);
displays[displayIndex].setFont(&FreeSansBold9pt7b);
displays[displayIndex].println(F("SSID:"));
displays[displayIndex].setFont(&FreeSans9pt7b);
displays[displayIndex].println(ssid);
displays[displayIndex].println("");
displays[displayIndex].setFont(&FreeSansBold9pt7b);
displays[displayIndex].println(F("Password:"));
displays[displayIndex].setFont(&FreeSans9pt7b);
displays[displayIndex].println(password);
displays[displayIndex].display(true);
displayIndex = 2;
displays[displayIndex].setPartialWindow(0, 0, displays[displayIndex].width(), displays[displayIndex].height());
displays[displayIndex].fillScreen(GxEPD_WHITE);
displays[displayIndex].setTextColor(GxEPD_BLACK);
displays[displayIndex].setCursor(0, 50);
displays[displayIndex].setFont(&FreeSans9pt7b);
displays[displayIndex].println(F("To setup\r\nscan QR or\r\nconnect\r\nmanually"));
// displays[displayIndex].println(F("scan QR or"));
// displays[displayIndex].println(F("connect"));
// displays[displayIndex].println(F("manually"));
displays[displayIndex].display(true);
displayIndex = 0;
displays[displayIndex].setPartialWindow(0, 0, displays[displayIndex].width(), displays[displayIndex].height());
displays[displayIndex].fillScreen(GxEPD_WHITE);
displays[displayIndex].setTextColor(GxEPD_BLACK);
displays[displayIndex].setCursor(0, 50);
displays[displayIndex].setFont(&FreeSansBold9pt7b);
displays[displayIndex].println(F("Welcome!"));
displays[displayIndex].display(true);
for (int i = 1; i < NUM_SCREENS; (i = i + 2))
{
displays[i].setPartialWindow(0, 0, displays[i].width(), displays[i].height());
displays[i].fillScreen(GxEPD_WHITE);
displays[i].display(true);
}
for (int i = 0; i < NUM_SCREENS; i++)
{
displays[i].hibernate();
}
}

View File

@ -1,4 +1,20 @@
#pragma once
#include "config.h"
#include "shared.hpp"
#include "qrcodegen.h"
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSans9pt7b.h>
#ifdef IS_BW
#include <GxEPD2_BW.h>
#else
#include <GxEPD2_3C.h>
#endif
int getBgColor(); int getBgColor();
int getFgColor(); int getFgColor();
void setBgColor(int color); void setBgColor(int color);
void setFgColor(int color); void setFgColor(int color);
void showSetupQr(const String& ssid, const String& password);

View File

@ -7,7 +7,7 @@ std::map<int, std::string> screenNameMap;
#ifndef NO_MCP #ifndef NO_MCP
Adafruit_MCP23X17 mcp; Adafruit_MCP23X17 mcp;
const int MCP_INT_PIN = 8; const char MCP_INT_PIN = 8;
#endif #endif
bool timerRunning = true; bool timerRunning = true;
@ -27,7 +27,7 @@ Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
String softAP_SSID; String softAP_SSID;
String softAP_password; String softAP_password;
WiFiMulti wifiMulti; //WiFiMulti wifiMulti;
WiFiManager wm; WiFiManager wm;
bool screenVisible[5]; bool screenVisible[5];
@ -36,20 +36,20 @@ void setupSoftAP()
{ {
byte mac[6]; byte mac[6];
WiFi.macAddress(mac); WiFi.macAddress(mac);
softAP_SSID = String("BTClock" + String(mac[5], 16) + String(mac[6], 16)); softAP_SSID = String("BTClock" + String(mac[5], 16) + String(mac[1], 16));
WiFi.setHostname(softAP_SSID.c_str()); 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); softAP_password = base64::encode(String(mac[2], 16) + String(mac[4], 16) + String(mac[5], 16) + String(mac[1], 16)).substring(2, 10);
} }
void setupComponents() void setupComponents()
{ {
if (psramInit()) if (psramInit())
{ {
Serial.println("\nPSRAM is correctly initialized"); Serial.println(F("PSRAM is correctly initialized"));
} }
else else
{ {
Serial.println("PSRAM not available"); Serial.println(F("PSRAM not available"));
} }
#ifdef WITH_RGB_LED #ifdef WITH_RGB_LED
pixels.begin(); pixels.begin();
@ -61,12 +61,12 @@ void setupComponents()
#endif #endif
// delay(3000); // delay(3000);
// Serial.println("Leds should be on"); // Serial.println(F("Leds should be on"));
#ifndef NO_MCP #ifndef NO_MCP
if (!mcp.begin_I2C()) if (!mcp.begin_I2C())
{ {
Serial.println("Error MCP23017"); Serial.println(F("Error MCP23017"));
pixels.setPixelColor(0, pixels.Color(255, 0, 0)); pixels.setPixelColor(0, pixels.Color(255, 0, 0));
pixels.setPixelColor(1, pixels.Color(255, 0, 0)); pixels.setPixelColor(1, pixels.Color(255, 0, 0));
pixels.setPixelColor(2, pixels.Color(255, 0, 0)); pixels.setPixelColor(2, pixels.Color(255, 0, 0));
@ -77,7 +77,7 @@ void setupComponents()
} }
else else
{ {
Serial.println("MCP23017 ok"); Serial.println(F("MCP23017 ok"));
pixels.setPixelColor(0, pixels.Color(0, 255, 0)); pixels.setPixelColor(0, pixels.Color(0, 255, 0));
pixels.setPixelColor(1, pixels.Color(0, 255, 0)); pixels.setPixelColor(1, pixels.Color(0, 255, 0));
pixels.setPixelColor(2, pixels.Color(0, 255, 0)); pixels.setPixelColor(2, pixels.Color(0, 255, 0));
@ -107,7 +107,7 @@ void synchronizeTime()
{ {
configTime(preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS), 0, NTP_SERVER); configTime(preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS), 0, NTP_SERVER);
delay(500); delay(500);
Serial.println("Retry set time"); Serial.println(F("Retry set time"));
} }
rtc.setTimeStruct(timeinfo); rtc.setTimeStruct(timeinfo);
@ -134,7 +134,7 @@ void setupWifi()
pixels.setPixelColor(2, pixels.Color(255, 0, 0)); pixels.setPixelColor(2, pixels.Color(255, 0, 0));
pixels.setPixelColor(3, pixels.Color(0, 0, 255)); pixels.setPixelColor(3, pixels.Color(0, 0, 255));
pixels.show(); pixels.show();
Serial.println("Erasing WiFi Config, restarting"); Serial.println(F("Erasing WiFi Config, restarting"));
wm.resetSettings(); wm.resetSettings();
ESP.restart(); ESP.restart();
} }
@ -252,7 +252,7 @@ void toggleScreenTimer()
if (!timerRunning) if (!timerRunning)
{ {
Serial.println("Stopping screen timer..."); Serial.println(F("Stopping screen timer..."));
for (int i = NEOPIXEL_COUNT; i >= 0; i--) for (int i = NEOPIXEL_COUNT; i >= 0; i--)
{ {
for (int j = NEOPIXEL_COUNT; j >= 0; j--) for (int j = NEOPIXEL_COUNT; j >= 0; j--)
@ -275,7 +275,7 @@ void toggleScreenTimer()
} }
else else
{ {
Serial.println("Starting screen timer..."); Serial.println(F("Starting screen timer..."));
pixels.setPixelColor(3, pixels.Color(0, 255, 0)); pixels.setPixelColor(3, pixels.Color(0, 255, 0));
pixels.setPixelColor(2, pixels.Color(0, 0, 0)); pixels.setPixelColor(2, pixels.Color(0, 0, 0));
@ -442,7 +442,7 @@ void setupI2C()
if (slaveMode) if (slaveMode)
{ {
Serial.println("I2C Slave Mode enabled"); Serial.println(F("I2C Slave Mode enabled"));
Wire.onReceive(onI2CReceive); Wire.onReceive(onI2CReceive);
Wire.begin((uint8_t)I2C_DEV_ADDR); Wire.begin((uint8_t)I2C_DEV_ADDR);
} }
@ -461,5 +461,5 @@ void onI2CReceive(int len)
void onI2CRequest() void onI2CRequest()
{ {
Wire.print("I2C Packets."); Wire.print("I2C Packets.");
Serial.println("onRequest"); Serial.println(F("onRequest"));
} }

View File

@ -2,7 +2,7 @@
#include <WiFi.h> #include <WiFi.h>
#include <Arduino.h> #include <Arduino.h>
#include <WiFiManager.h> #include <WiFiManager.h>
#include <WiFiMulti.h> //#include <WiFiMulti.h>
#include "config.h" #include "config.h"
#include "shared.hpp" #include "shared.hpp"

View File

@ -9,10 +9,7 @@ void setupWebserver()
// Initialize SPIFFS // Initialize SPIFFS
if (!SPIFFS.begin(true)) if (!SPIFFS.begin(true))
{ {
pinMode(47, OUTPUT); Serial.println(F("An Error has occurred while mounting SPIFFS"));
digitalWrite(47, HIGH);
Serial.println("An Error has occurred while mounting SPIFFS");
return; return;
} }
@ -57,14 +54,14 @@ void setupWebserver()
server.begin(); server.begin();
if (!MDNS.begin(HOSTNAME)) if (!MDNS.begin(HOSTNAME))
{ {
Serial.println("Error setting up MDNS responder!"); Serial.println(F("Error setting up MDNS responder!"));
while (1) while (1)
{ {
delay(1000); delay(1000);
} }
} }
MDNS.addService("http", "tcp", 80); MDNS.addService("http", "tcp", 80);
Serial.println("Webserver should be running"); Serial.println(F("Webserver should be running"));
} }
/** /**
@ -112,7 +109,7 @@ void onApiStatus(AsyncWebServerRequest *request)
void onApiActionPause(AsyncWebServerRequest *request) void onApiActionPause(AsyncWebServerRequest *request)
{ {
timerRunning = false; timerRunning = false;
Serial.println("Update timer paused"); Serial.println(F("Update timer paused"));
request->send(200); request->send(200);
}; };
@ -137,7 +134,7 @@ void onApiActionTimerRestart(AsyncWebServerRequest *request)
{ {
// moment = millis(); // moment = millis();
timerRunning = true; timerRunning = true;
Serial.println("Update timer restarted"); Serial.println(F("Update timer restarted"));
request->send(200); request->send(200);
} }
@ -221,7 +218,7 @@ bool processEpdColorSettings(AsyncWebServerRequest *request)
AsyncWebParameter *fgColor = request->getParam("fgColor", true); AsyncWebParameter *fgColor = request->getParam("fgColor", true);
preferences.putUInt("fgColor", strtol(fgColor->value().c_str(), NULL, 16)); preferences.putUInt("fgColor", strtol(fgColor->value().c_str(), NULL, 16));
setFgColor(int(strtol(fgColor->value().c_str(), NULL, 16))); setFgColor(int(strtol(fgColor->value().c_str(), NULL, 16)));
Serial.print("Setting foreground color to "); Serial.print(F("Setting foreground color to "));
Serial.println(fgColor->value().c_str()); Serial.println(fgColor->value().c_str());
settingsChanged = true; settingsChanged = true;
} }
@ -231,7 +228,7 @@ bool processEpdColorSettings(AsyncWebServerRequest *request)
preferences.putUInt("bgColor", strtol(bgColor->value().c_str(), NULL, 16)); preferences.putUInt("bgColor", strtol(bgColor->value().c_str(), NULL, 16));
setBgColor(int(strtol(bgColor->value().c_str(), NULL, 16))); setBgColor(int(strtol(bgColor->value().c_str(), NULL, 16)));
Serial.print("Setting background color to "); Serial.print(F("Setting background color to "));
Serial.println(bgColor->value().c_str()); Serial.println(bgColor->value().c_str());
settingsChanged = true; settingsChanged = true;
} }
@ -250,7 +247,7 @@ void onApiEpdSettingsPost(AsyncWebServerRequest *request)
{ {
flashTemporaryLights(0, 255, 0); flashTemporaryLights(0, 255, 0);
Serial.println("Settings changed"); Serial.println(F("Settings changed"));
} }
} }
@ -384,7 +381,7 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
{ {
flashTemporaryLights(0, 255, 0); flashTemporaryLights(0, 255, 0);
Serial.println("Settings changed"); Serial.println(F("Settings changed"));
} }
} }
@ -504,7 +501,7 @@ void onApiLightsFlash(AsyncWebServerRequest *request)
void onApiLightsSetColor(AsyncWebServerRequest *request) void onApiLightsSetColor(AsyncWebServerRequest *request)
{ {
String rgbColor = request->pathArg(0); String rgbColor = request->pathArg(0);
int r, g, b; uint r, g, b;
sscanf(rgbColor.c_str(), "%02x%02x%02x", &r, &g, &b); sscanf(rgbColor.c_str(), "%02x%02x%02x", &r, &g, &b);
setLights(r, g, b); setLights(r, g, b);
request->send(200, "text/plain", rgbColor); request->send(200, "text/plain", rgbColor);

View File

@ -18,7 +18,7 @@
#include "screens/blockheight.hpp" #include "screens/blockheight.hpp"
#include "screens/ticker.hpp" #include "screens/ticker.hpp"
#include "screens/time.hpp" #include "screens/time.hpp"
#include "screens/sats_per_dollar.hpp" //#include "screens/sats_per_dollar.hpp"
#include "screens/halvingcountdown.hpp" #include "screens/halvingcountdown.hpp"
#include "tasks/ha.hpp" #include "tasks/ha.hpp"
@ -26,7 +26,7 @@
#include "tasks/button.hpp" #include "tasks/button.hpp"
#include "tasks/led_handler.hpp" #include "tasks/led_handler.hpp"
WiFiClient wifiClientInsecure; //WiFiClient wifiClientInsecure;
WiFiClientSecure wifiClient; WiFiClientSecure wifiClient;
ESP32Time rtc(3600); ESP32Time rtc(3600);
@ -63,8 +63,9 @@ void setup()
TimeScreen::init(); TimeScreen::init();
BlockHeightScreen::init(); BlockHeightScreen::init();
HalvingCountdownScreen::init();
TickerScreen::init(); TickerScreen::init();
SatsPerDollarScreen::init(); // SatsPerDollarScreen::init();
#ifdef WITH_BUTTONS #ifdef WITH_BUTTONS
setupButtonTask(); setupButtonTask();
@ -79,7 +80,7 @@ void setup()
registerNewBlockCallback(BlockHeightScreen::onNewBlock); registerNewBlockCallback(BlockHeightScreen::onNewBlock);
registerNewBlockCallback(HalvingCountdownScreen::onNewBlock); registerNewBlockCallback(HalvingCountdownScreen::onNewBlock);
registerNewPriceCallback(TickerScreen::onPriceUpdate); registerNewPriceCallback(TickerScreen::onPriceUpdate);
registerNewPriceCallback(SatsPerDollarScreen::onPriceUpdate); // registerNewPriceCallback(SatsPerDollarScreen::onPriceUpdate);
setupDisplays(); setupDisplays();
} else { } else {

View File

@ -1,13 +1,23 @@
#include "blockheight.hpp" #include "blockheight.hpp"
uint BlockHeightScreen::blockNr = 0; uint BlockHeightScreen::blockNr = 0;
std::array<String, NUM_SCREENS> BlockHeightScreen::epdContent = { "", "", "", "", "", "", "" }; char **BlockHeightScreen::epdContent;
const int maxStringLength = 15;
//std::array<String, NUM_SCREENS> * BlockHeightScreen::epdContent = (std::array<String, NUM_SCREENS> * ) ps_malloc(7 * sizeof (std::array<String, NUM_SCREENS>)); char* BlockHeightScreen::psramBuffer;
void BlockHeightScreen::init() void BlockHeightScreen::init()
{ {
BlockHeightScreen::psramBuffer = (char*)ps_malloc(NUM_SCREENS * maxStringLength);
if (BlockHeightScreen::psramBuffer == nullptr)
{
Serial.println(F("Failed to allocate memory in PSRAM"));
}
BlockHeightScreen::epdContent = new char*[NUM_SCREENS];
for (int i = 0; i < NUM_SCREENS; i++)
{
epdContent[i] = psramBuffer + i * maxStringLength;
}
BlockHeightScreen::blockNr = preferences.getUInt("blockHeight", 789000); BlockHeightScreen::blockNr = preferences.getUInt("blockHeight", 789000);
setupBlockNotify(); setupBlockNotify();
BlockHeightScreen::showScreen(); BlockHeightScreen::showScreen();
@ -17,10 +27,14 @@ void BlockHeightScreen::showScreen()
{ {
std::string blockNrString = String(BlockHeightScreen::blockNr).c_str(); std::string blockNrString = String(BlockHeightScreen::blockNr).c_str();
blockNrString.insert(blockNrString.begin(), NUM_SCREENS - blockNrString.length(), ' '); blockNrString.insert(blockNrString.begin(), NUM_SCREENS - blockNrString.length(), ' ');
epdContent[0] = "BLOCK/HEIGHT"; // epdContent[0] = "BLOCK/HEIGHT";
snprintf(BlockHeightScreen::epdContent[0], 13, "BLOCK/HEIGHT", 0);
for (uint i = 1; i < NUM_SCREENS; i++) for (uint i = 1; i < NUM_SCREENS; i++)
{ {
BlockHeightScreen::epdContent[i] = blockNrString[i]; snprintf(BlockHeightScreen::epdContent[i], 2, &blockNrString[i], 0);
// BlockHeightScreen::epdContent[i] = blockNrString[i];
} }
} }
@ -31,6 +45,16 @@ void BlockHeightScreen::onNewBlock(uint blockNr)
BlockHeightScreen::showScreen(); BlockHeightScreen::showScreen();
} }
std::array<String, 7> BlockHeightScreen::getEpdContent() { std::array<String, NUM_SCREENS> BlockHeightScreen::getEpdContent()
return BlockHeightScreen::epdContent; {
std::array<String, NUM_SCREENS> ret;
for (int i = 0; i < NUM_SCREENS; i++)
{
ret[i] = BlockHeightScreen::epdContent[i];
}
// std::copy(std::begin(BlockHeightScreen::epdContent), std::end(BlockHeightScreen::epdContent), std::begin(ret));
return ret;
} }

View File

@ -6,11 +6,13 @@
#include "tasks/epd.hpp" #include "tasks/epd.hpp"
#include "tasks/blocknotify.hpp" #include "tasks/blocknotify.hpp"
class BlockHeightScreen { class BlockHeightScreen
protected: {
protected:
static uint blockNr; static uint blockNr;
static std::array<String, NUM_SCREENS> epdContent; static char** epdContent;
public: static char* psramBuffer;
public:
static void init(); static void init();
static void showScreen(); static void showScreen();
static void onNewBlock(uint blockNr); static void onNewBlock(uint blockNr);

View File

@ -38,6 +38,6 @@ void CountdownScreen::countdownTask(void *pvParameters)
} }
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
} }
Serial.println("Countdown finished!"); Serial.println(F("Countdown finished!"));
vTaskDelete(NULL); vTaskDelete(NULL);
} }

View File

@ -3,33 +3,63 @@
uint HalvingCountdownScreen::currentBlockNr = 0; uint HalvingCountdownScreen::currentBlockNr = 0;
uint HalvingCountdownScreen::halvingBlockNr = 0; uint HalvingCountdownScreen::halvingBlockNr = 0;
std::array<String, NUM_SCREENS> HalvingCountdownScreen::epdContent = {"", "", "", "", "", "", ""}; char **HalvingCountdownScreen::epdContentP;
const int maxStringLength = 12;
char *HalvingCountdownScreen::psramBuffer;
//char HalvingCountdownScreen::epdContent[NUM_SCREENS][maxStringLength];
bool initialized = false;
void HalvingCountdownScreen::init() void HalvingCountdownScreen::init()
{ {
HalvingCountdownScreen::currentBlockNr = preferences.getUInt("blockHeight", 789000); HalvingCountdownScreen::currentBlockNr = preferences.getUInt("blockHeight", 789000);
HalvingCountdownScreen::psramBuffer = (char *)ps_malloc(NUM_SCREENS * maxStringLength);
if (HalvingCountdownScreen::psramBuffer == nullptr)
{
Serial.println(F("Failed to allocate memory in PSRAM (HalvingCountdownScreen)"));
}
HalvingCountdownScreen::epdContentP = new char*[NUM_SCREENS];
for (int i = 0; i < NUM_SCREENS; i++)
{
epdContentP[i] = HalvingCountdownScreen::psramBuffer + i * maxStringLength;
// strcpy(epdContent[i], "x");
strcpy(epdContentP[i], "x");
}
initialized = true;
setupBlockNotify(); setupBlockNotify();
HalvingCountdownScreen::showScreen(); HalvingCountdownScreen::showScreen();
} }
void HalvingCountdownScreen::showScreen() void HalvingCountdownScreen::showScreen()
{ {
if (!initialized)
return;
uint minutesToHalving = HalvingCountdownScreen::getNextHalvingBlockNr() * 10; uint minutesToHalving = HalvingCountdownScreen::getNextHalvingBlockNr() * 10;
int years = floor(minutesToHalving / 525600); const int years = floor(minutesToHalving / 525600);
int days = floor((minutesToHalving - (years * 525600)) / (24*60)); const int days = floor((minutesToHalving - (years * 525600)) / (24 * 60));
int hours = floor((minutesToHalving - (years * 525600) - (days * (24*60))) / 60); const int hours = floor((minutesToHalving - (years * 525600) - (days * (24 * 60))) / 60);
int mins = floor(minutesToHalving - (years * 525600) - (days * (24*60)) - (hours * 60)); const int mins = floor(minutesToHalving - (years * 525600) - (days * (24 * 60)) - (hours * 60));
// int secs = floor((minutesToHalving - (years * 525600) - (days * (24*60)) - (hours * 60) - mins) * 60); // int secs = floor((minutesToHalving - (years * 525600) - (days * (24*60)) - (hours * 60) - mins) * 60);
epdContent[0] = F("BIT/COIN"); snprintf(HalvingCountdownScreen::epdContentP[0], maxStringLength, "BIT/COIN");
epdContent[1] = F("HALV/ING"); snprintf(HalvingCountdownScreen::epdContentP[1], maxStringLength, "HALV/ING");
epdContent[2] = String(years) + "/YRS"; snprintf(HalvingCountdownScreen::epdContentP[2], maxStringLength, "%d/YRS", years);
epdContent[3] = String(days) + "/DAYS";
epdContent[4] = String(hours) + "/HRS"; snprintf(HalvingCountdownScreen::epdContentP[3], maxStringLength, "%d/DAYS", days);
epdContent[5] = String(mins) + "/MINS"; snprintf(HalvingCountdownScreen::epdContentP[4], maxStringLength, "%d/HRS", hours);
epdContent[6] = F("TO/GO"); snprintf(HalvingCountdownScreen::epdContentP[5], maxStringLength, "%d/MINS", mins);
snprintf(HalvingCountdownScreen::epdContentP[6], maxStringLength, "TO/GO");
// // strcpy(epdContent[2], sprintf(String(years) + "/YRS").c_str());
// // snprintf(epdContent[2], sizeof(epdContent[2]), "%d/YRS", years);
// // strcpy(epdContent[3], String(days) + "/DAYS");
// // strcpy(epdContent[4], String(hours) + "/HRS");
// // strcpy(epdContent[5], String(mins) + "/MINS");
} }
uint HalvingCountdownScreen::getNextHalvingBlockNr() uint HalvingCountdownScreen::getNextHalvingBlockNr()
@ -46,5 +76,14 @@ void HalvingCountdownScreen::onNewBlock(uint blockNr)
std::array<String, NUM_SCREENS> HalvingCountdownScreen::getEpdContent() std::array<String, NUM_SCREENS> HalvingCountdownScreen::getEpdContent()
{ {
return HalvingCountdownScreen::epdContent; std::array<String, NUM_SCREENS> ret;
if (!initialized) return ret;
for (int i = 0; i < NUM_SCREENS; i++)
{
ret[i] = HalvingCountdownScreen::epdContentP[i];
}
return ret;
} }

View File

@ -6,12 +6,18 @@
#include "tasks/epd.hpp" #include "tasks/epd.hpp"
#include "tasks/blocknotify.hpp" #include "tasks/blocknotify.hpp"
class HalvingCountdownScreen { class HalvingCountdownScreen
protected: {
protected:
static uint currentBlockNr; static uint currentBlockNr;
static uint halvingBlockNr; static uint halvingBlockNr;
static std::array<String, NUM_SCREENS> epdContent; //static std::array<char*, NUM_SCREENS> epdContent;
public: // static char epdContent[NUM_SCREENS][12];
static char** epdContentP;
// static char **epdContent;
static char *psramBuffer;
public:
static void init(); static void init();
static void showScreen(); static void showScreen();
static void onNewBlock(uint blockNr); static void onNewBlock(uint blockNr);

View File

@ -1,30 +1,30 @@
#include "sats_per_dollar.hpp" // #include "sats_per_dollar.hpp"
uint SatsPerDollarScreen::satsPerDollar = 0; // uint SatsPerDollarScreen::satsPerDollar = 0;
std::array<String, NUM_SCREENS> SatsPerDollarScreen::epdContent = { "", "", "", "", "", "", "" }; // std::array<String, NUM_SCREENS> SatsPerDollarScreen::epdContent = { "", "", "", "", "", "", "" };
void SatsPerDollarScreen::init() { // void SatsPerDollarScreen::init() {
SatsPerDollarScreen::satsPerDollar = int(round(1 / preferences.getFloat("btcPrice", 12345) * 10e7)); // SatsPerDollarScreen::satsPerDollar = int(round(1 / preferences.getFloat("btcPrice", 12345) * 10e7));
setupGetPriceTask(); // setupGetPriceTask();
SatsPerDollarScreen::showScreen(); // SatsPerDollarScreen::showScreen();
} // }
void SatsPerDollarScreen::showScreen() { // void SatsPerDollarScreen::showScreen() {
std::string satsPerDollarString = String(SatsPerDollarScreen::satsPerDollar).c_str(); // std::string satsPerDollarString = String(SatsPerDollarScreen::satsPerDollar).c_str();
satsPerDollarString.insert(satsPerDollarString.begin(), 7 - satsPerDollarString.length(), ' '); // satsPerDollarString.insert(satsPerDollarString.begin(), 7 - satsPerDollarString.length(), ' ');
epdContent[0] = "MSCW/TIME"; // epdContent[0] = "MSCW/TIME";
for (uint i = 1; i < NUM_SCREENS; i++) // for (uint i = 1; i < NUM_SCREENS; i++)
{ // {
SatsPerDollarScreen::epdContent[i] = satsPerDollarString[i]; // SatsPerDollarScreen::epdContent[i] = satsPerDollarString[i];
} // }
} // }
void SatsPerDollarScreen::onPriceUpdate(uint price) { // void SatsPerDollarScreen::onPriceUpdate(uint price) {
SatsPerDollarScreen::satsPerDollar = int(round(1 / float(price) * 10e7)); // SatsPerDollarScreen::satsPerDollar = int(round(1 / float(price) * 10e7));
SatsPerDollarScreen::showScreen(); // SatsPerDollarScreen::showScreen();
} // }
std::array<String, NUM_SCREENS> SatsPerDollarScreen::getEpdContent() { // std::array<String, NUM_SCREENS> SatsPerDollarScreen::getEpdContent() {
return SatsPerDollarScreen::epdContent; // return SatsPerDollarScreen::epdContent;
} // }

View File

@ -1,17 +1,17 @@
#pragma once // #pragma once
#include "base.hpp" // #include "base.hpp"
#include "config.h" // #include "config.h"
#include "shared.hpp" // #include "shared.hpp"
#include "tasks/epd.hpp" // #include "tasks/epd.hpp"
class SatsPerDollarScreen { // class SatsPerDollarScreen {
protected: // protected:
static uint satsPerDollar; // static uint satsPerDollar;
static std::array<String, NUM_SCREENS> epdContent; // static std::array<String, NUM_SCREENS> epdContent;
public: // public:
static void init(); // static void init();
static void showScreen(); // static void showScreen();
static void onPriceUpdate(uint price); // static void onPriceUpdate(uint price);
static std::array<String, NUM_SCREENS> getEpdContent(); // static std::array<String, NUM_SCREENS> getEpdContent();
}; // };

View File

@ -1,10 +1,15 @@
#include "ticker.hpp" #include "ticker.hpp"
uint TickerScreen::price = 12345; uint TickerScreen::price = 12345;
uint TickerScreen::satsPerDollar = 3000;
std::array<String, NUM_SCREENS> TickerScreen::epdContent = { "", "", "", "", "", "", "" }; std::array<String, NUM_SCREENS> TickerScreen::epdContent = { "", "", "", "", "", "", "" };
void TickerScreen::init() { void TickerScreen::init() {
TickerScreen::price = preferences.getFloat("btcPrice", 12345);; TickerScreen::price = preferences.getFloat("btcPrice", 12345);;
TickerScreen::satsPerDollar = int(round(1 / preferences.getFloat("btcPrice", 12345) * 10e7));
setupGetPriceTask(); setupGetPriceTask();
TickerScreen::showScreen(); TickerScreen::showScreen();
} }
@ -21,9 +26,26 @@ void TickerScreen::showScreen() {
void TickerScreen::onPriceUpdate(uint price) { void TickerScreen::onPriceUpdate(uint price) {
TickerScreen::price = price; TickerScreen::price = price;
TickerScreen::satsPerDollar = int(round(1 / float(price) * 10e7));
TickerScreen::showScreen(); TickerScreen::showScreen();
} }
std::array<String, NUM_SCREENS> TickerScreen::getEpdContent() { std::array<String, NUM_SCREENS> TickerScreen::getEpdContent() {
return TickerScreen::epdContent; return TickerScreen::epdContent;
} }
std::array<String, NUM_SCREENS> TickerScreen::getEpdContentSats() {
std::array<String, NUM_SCREENS> epdContentSats = { "", "", "", "", "", "", "" };
std::string satsPerDollarString = String(TickerScreen::satsPerDollar).c_str();
satsPerDollarString.insert(satsPerDollarString.begin(), NUM_SCREENS - satsPerDollarString.length(), ' ');
epdContentSats[0] = "MSCW/TIME";
for (uint i = 1; i < NUM_SCREENS; i++)
{
epdContentSats[i] = satsPerDollarString[i];
}
return epdContentSats;
}

View File

@ -9,11 +9,13 @@ class TickerScreen
{ {
protected: protected:
static uint price; static uint price;
static std::array<String, NUM_SCREENS> epdContent; static uint satsPerDollar;
static std::array<String, NUM_SCREENS> epdContent;
public: public:
static void init(); static void init();
static void showScreen(); static void showScreen();
static void onPriceUpdate(uint price); static void onPriceUpdate(uint price);
static std::array<String, NUM_SCREENS> getEpdContent(); static std::array<String, NUM_SCREENS> getEpdContent();
static std::array<String, NUM_SCREENS> getEpdContentSats();
}; };

View File

@ -1,11 +1,18 @@
#pragma once #pragma once
#include "config.h"
#include <Arduino.h> #include <Arduino.h>
//##include <Crypto.h> //##include <Crypto.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <WiFi.h> #include <WiFi.h>
#include <map> #include <map>
#ifdef IS_BW
#include <GxEPD2_BW.h>
#else
#include <GxEPD2_3C.h>
#endif
#include <WiFiClientSecure.h> #include <WiFiClientSecure.h>
#ifndef NO_MCP #ifndef NO_MCP
#include <Adafruit_MCP23X17.h> #include <Adafruit_MCP23X17.h>
@ -19,7 +26,7 @@
typedef std::function<void()> EventCallback; typedef std::function<void()> EventCallback;
typedef std::function<void(uint number)> EventCallbackWithNumber; typedef std::function<void(uint number)> EventCallbackWithNumber;
extern WiFiClient wifiClientInsecure; //extern WiFiClient wifiClientInsecure;
extern WiFiClientSecure wifiClient; extern WiFiClientSecure wifiClient;
extern ESP32Time rtc; extern ESP32Time rtc;
@ -32,9 +39,11 @@ extern bool timerRunning;
extern uint timerSeconds; extern uint timerSeconds;
extern uint32_t moment; extern uint32_t moment;
extern GxEPD2_BW<GxEPD2_213_B74, GxEPD2_213_B74::HEIGHT> displays[NUM_SCREENS];
#ifndef NO_MCP #ifndef NO_MCP
extern Adafruit_MCP23X17 mcp; extern Adafruit_MCP23X17 mcp;
extern const int MCP_INT_PIN; extern const char MCP_INT_PIN;
#endif #endif
#ifdef WITH_RGB_LED #ifdef WITH_RGB_LED
extern Adafruit_NeoPixel pixels; extern Adafruit_NeoPixel pixels;
@ -54,11 +63,15 @@ const uint screenCount = sizeof(screens) / sizeof(int);
struct SpiRamAllocator { struct SpiRamAllocator {
void* allocate(size_t size) { void* allocate(size_t size) {
return ps_malloc(size); return heap_caps_malloc(size, MALLOC_CAP_SPIRAM);
} }
void deallocate(void* pointer) { void deallocate(void* pointer) {
free(pointer); heap_caps_free(pointer);
}
void* reallocate(void* ptr, size_t new_size) {
return heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM);
} }
}; };

View File

@ -20,11 +20,11 @@ void checkBitcoinBlock(void *pvParameters)
uint blockHeight = preferences.getUInt("blockHeight", currentBlockHeight); uint blockHeight = preferences.getUInt("blockHeight", currentBlockHeight);
useBitcoind = preferences.getBool("useNode", false) && wifiClientInsecure.connect(preferences.getString("rpcHost", BITCOIND_HOST).c_str(), preferences.getUInt("rpcPort", BITCOIND_PORT)); useBitcoind = preferences.getBool("useNode", false) && wifiClient.connect(preferences.getString("rpcHost", BITCOIND_HOST).c_str(), preferences.getUInt("rpcPort", BITCOIND_PORT));
if (useBitcoind) if (useBitcoind)
Serial.println("bitcoind node is reachable, using this for blocks."); Serial.println(F("bitcoind node is reachable, using this for blocks."));
else else
Serial.println("bitcoind node is not reachable, using mempool API instead."); Serial.println(F("bitcoind node is not reachable, using mempool API instead."));
IPAddress result; IPAddress result;
@ -36,53 +36,53 @@ void checkBitcoinBlock(void *pvParameters)
for (;;) for (;;)
{ {
HTTPClient http; HTTPClient *http = new HTTPClient();
http.setUserAgent(USER_AGENT); http->setUserAgent(USER_AGENT);
if (useBitcoind) if (useBitcoind)
{ {
StaticJsonDocument<200> jsonDoc; StaticJsonDocument<200> jsonDoc;
http.begin(preferences.getString("rpcHost", BITCOIND_HOST).c_str(), preferences.getUInt("rpcPort", BITCOIND_PORT)); http->begin(preferences.getString("rpcHost", BITCOIND_HOST).c_str(), preferences.getUInt("rpcPort", BITCOIND_PORT));
http.addHeader("Content-Type", "application/json"); http->addHeader("Content-Type", "application/json");
const String payload = "{\"jsonrpc\":\"1.0\",\"id\":\"current_block_height\",\"method\":\"getblockcount\",\"params\":[]}"; const String payload = "{\"jsonrpc\":\"1.0\",\"id\":\"current_block_height\",\"method\":\"getblockcount\",\"params\":[]}";
const String authEncoded = base64::encode(preferences.getString("rpcUser", BITCOIND_RPC_USER) + ":" + preferences.getString("rpcPass", BITCOIND_RPC_PASS)); const String authEncoded = base64::encode(preferences.getString("rpcUser", BITCOIND_RPC_USER) + ":" + preferences.getString("rpcPass", BITCOIND_RPC_PASS));
http.addHeader("Authorization", "Basic " + authEncoded); http->addHeader("Authorization", "Basic " + authEncoded);
int httpCode = http.POST(payload); int httpCode = http->POST(payload);
if (httpCode > 0 || httpCode != HTTP_CODE_UNAUTHORIZED) if (httpCode > 0 || httpCode != HTTP_CODE_UNAUTHORIZED)
{ {
String response = http.getString(); String response = http->getString();
deserializeJson(jsonDoc, response); deserializeJson(jsonDoc, response);
blockHeight = jsonDoc["result"]; blockHeight = jsonDoc["result"];
} }
else else
{ {
Serial.println("Error in HTTP request to bitcoind"); Serial.println(F("Error in HTTP request to bitcoind"));
} }
http.end(); http->end();
} }
else else
{ {
http.begin("https://" + preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE) + "/api/blocks/tip/height"); http->begin("https://" + preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE) + "/api/blocks/tip/height");
int httpCode = http.GET(); int httpCode = http->GET();
if (httpCode > 0 && httpCode == HTTP_CODE_OK) if (httpCode > 0 && httpCode == HTTP_CODE_OK)
{ {
String blockHeightStr = http.getString(); String blockHeightStr = http->getString();
blockHeight = blockHeightStr.toInt(); blockHeight = blockHeightStr.toInt();
} }
else else
{ {
Serial.print(F("Error in HTTP request to mempool API: ")); Serial.print(F("Error in HTTP request to mempool API: "));
Serial.print(httpCode); Serial.print(httpCode);
Serial.println(http.errorToString(httpCode)); Serial.println(http->errorToString(httpCode));
} }
http.end(); http->end();
} }
if (blockHeight > currentBlockHeight) if (blockHeight > currentBlockHeight)
{ {
@ -93,7 +93,7 @@ void checkBitcoinBlock(void *pvParameters)
currentBlockHeight = blockHeight; currentBlockHeight = blockHeight;
preferences.putUInt("blockHeight", currentBlockHeight); preferences.putUInt("blockHeight", currentBlockHeight);
} }
delete http;
vTaskDelay(pdMS_TO_TICKS(BLOCKNOTIFY_WAIT_TIME)); // wait 1 minute before checking again vTaskDelay(pdMS_TO_TICKS(BLOCKNOTIFY_WAIT_TIME)); // wait 1 minute before checking again
} }
} }
@ -126,7 +126,7 @@ void setupBlockNotify()
// xTaskCreate(bitcoinEventHandler, "bitcoinEventHandler", 10000, NULL, 110, NULL); // xTaskCreate(bitcoinEventHandler, "bitcoinEventHandler", 10000, NULL, 110, NULL);
} }
void registerNewBlockCallback(EventCallbackWithNumber cb) void registerNewBlockCallback(const EventCallbackWithNumber cb)
{ {
blockEventCallbacks.push_back(cb); blockEventCallbacks.push_back(cb);
} }

View File

@ -22,6 +22,6 @@ void checkBitcoinBlock(void *pvParameters);
//void bitcoinEventHandler(void *pvParameters); //void bitcoinEventHandler(void *pvParameters);
void setupBlockNotify(); void setupBlockNotify();
void registerNewBlockCallback(EventCallbackWithNumber cb); void registerNewBlockCallback(const EventCallbackWithNumber cb);
int getBlockFromBitcoind(); int getBlockFromBitcoind();
int getBlockFromMempoolSpace(); int getBlockFromMempoolSpace();

View File

@ -47,7 +47,7 @@ void buttonTask(void *parameter)
void IRAM_ATTR handleButtonInterrupt() void IRAM_ATTR handleButtonInterrupt()
{ {
buttonPressed = true; buttonPressed = true;
// Serial.println("ISR"); // Serial.println(F("ISR"));
// uint pin = mcp.getLastInterruptPin(); // uint pin = mcp.getLastInterruptPin();
// if (pin == 1) // if (pin == 1)
@ -70,7 +70,7 @@ void setupButtonTask()
// attachInterrupt(MCP_INT_PIN, handleButtonInterrupt, FALLING); // attachInterrupt(MCP_INT_PIN, handleButtonInterrupt, FALLING);
} }
void registerNewButtonCallback(EventCallback cb) void registerNewButtonCallback(const EventCallback cb)
{ {
buttonEventCallbacks.push_back(cb); buttonEventCallbacks.push_back(cb);
} }

View File

@ -14,7 +14,7 @@ extern TaskHandle_t buttonTaskHandle;
void buttonTask(void *pvParameters); void buttonTask(void *pvParameters);
void setupButtonTask(); void setupButtonTask();
void registerNewButtonCallback(EventCallback cb); void registerNewButtonCallback(const EventCallback cb);
void IRAM_ATTR handleButtonInterrupt(); void IRAM_ATTR handleButtonInterrupt();
#endif #endif

View File

@ -1,17 +1,12 @@
#include "epd.hpp" #include "epd.hpp"
#ifdef IS_S3 #ifdef IS_S3
// reversed const char EPD_CS[NUM_SCREENS] = {2, 4, 6, 10, 33, 21, 17};
// const int EPD_CS[7] = {17, 21, 33, 10, 6, 4, 2}; const char EPD_BUSY[NUM_SCREENS] = {3, 5, 7, 9, 37, 18, 16};
// const int EPD_BUSY[7] = {16, 18, 37, 9, 7, 5, 3}; const char EPD_RESET_MPD[NUM_SCREENS] = {8, 9, 10, 11, 12, 13, 14};
// const int EPD_RESET_MPD[7] = {14, 13, 12, 11, 10, 9, 8};
const int EPD_CS[NUM_SCREENS] = {2, 4, 6, 10, 33, 21, 17}; const char EPD_DC = 14;
const int EPD_BUSY[NUM_SCREENS] = {3, 5, 7, 9, 37, 18, 16}; const char RST_PIN = 15;
const int EPD_RESET_MPD[NUM_SCREENS] = {8, 9, 10, 11, 12, 13, 14};
const int EPD_DC = 14;
const int RST_PIN = 15;
#elif defined(IS_S2) #elif defined(IS_S2)
// reversed // reversed
@ -56,7 +51,7 @@ GxEPD2_BW<GxEPD2_213_B74, GxEPD2_213_B74::HEIGHT> displays[NUM_SCREENS] = {
GxEPD2_213_B74(EPD_CS[6], EPD_DC, /*RST=*/-1, EPD_BUSY[6]), GxEPD2_213_B74(EPD_CS[6], EPD_DC, /*RST=*/-1, EPD_BUSY[6]),
}; };
//GxEPD2_BW<GxEPD2_213_B74, GxEPD2_213_B74::HEIGHT> * displays2 = (GxEPD2_BW<GxEPD2_213_B74, GxEPD2_213_B74::HEIGHT> *) ps_malloc(7 * sizeof (GxEPD2_BW<GxEPD2_213_B74, GxEPD2_213_B74::HEIGHT>)); // GxEPD2_BW<GxEPD2_213_B74, GxEPD2_213_B74::HEIGHT> * displays2 = (GxEPD2_BW<GxEPD2_213_B74, GxEPD2_213_B74::HEIGHT> *) ps_malloc(7 * sizeof (GxEPD2_BW<GxEPD2_213_B74, GxEPD2_213_B74::HEIGHT>));
const int SEM_WAIT_TIME = 10000; const int SEM_WAIT_TIME = 10000;
@ -81,7 +76,10 @@ std::array<String, 7> currentEpdContent;
std::array<String, 7> epdContent; std::array<String, 7> epdContent;
TaskHandle_t tasks[NUM_SCREENS]; TaskHandle_t tasks[NUM_SCREENS];
SemaphoreHandle_t epdUpdateSemaphore[NUM_SCREENS]; SemaphoreHandle_t epdUpdateSemaphore[NUM_SCREENS];
uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; //
//int *qrcode = (int *) ps_malloc(qrcodegen_BUFFER_LEN_MAX * sizeof(uint8_t));
void setupDisplays() void setupDisplays()
{ {
@ -91,7 +89,7 @@ void setupDisplays()
void resetAllDisplays() void resetAllDisplays()
{ {
#ifdef NO_MCP #ifdef NO_MCP
digitalWrite(RST_PIN, HIGH); digitalWrite(RST_PIN, HIGH);
pinMode(RST_PIN, OUTPUT); pinMode(RST_PIN, OUTPUT);
delay(20); delay(20);
@ -99,23 +97,24 @@ void resetAllDisplays()
delay(20); delay(20);
digitalWrite(RST_PIN, HIGH); digitalWrite(RST_PIN, HIGH);
delay(200); delay(200);
#else #else
for (int i = 0; i < NUM_SCREENS; i++) { for (int i = 0; i < NUM_SCREENS; i++)
{
resetSingleDisplay(i); resetSingleDisplay(i);
} }
#endif NO_MCP #endif
} }
void resetSingleDisplay(int i) void resetSingleDisplay(int i)
{ {
#ifndef NO_MCP #ifndef NO_MCP
mcp.digitalWrite(EPD_RESET_MPD[i], HIGH); mcp.digitalWrite(EPD_RESET_MPD[i], HIGH);
delay(20); delay(20);
mcp.digitalWrite(EPD_RESET_MPD[i], LOW); mcp.digitalWrite(EPD_RESET_MPD[i], LOW);
delay(20); delay(20);
mcp.digitalWrite(EPD_RESET_MPD[i], HIGH); mcp.digitalWrite(EPD_RESET_MPD[i], HIGH);
delay(200); delay(200);
#endif #endif
} }
void initDisplays() void initDisplays()
@ -166,7 +165,7 @@ void taskEpd(void *pvParameters)
epdContent = TickerScreen::getEpdContent(); epdContent = TickerScreen::getEpdContent();
break; break;
case SCREEN_MSCW_TIME: case SCREEN_MSCW_TIME:
epdContent = SatsPerDollarScreen::getEpdContent(); epdContent = TickerScreen::getEpdContentSats();
break; break;
case SCREEN_TIME: case SCREEN_TIME:
epdContent = TimeScreen::getEpdContent(); epdContent = TimeScreen::getEpdContent();
@ -182,10 +181,8 @@ void taskEpd(void *pvParameters)
break; break;
} }
bool updatedThisCycle = false; bool updatedThisCycle = false;
for (uint i = 0; i < NUM_SCREENS; i++) for (uint i = 0; i < NUM_SCREENS; i++)
{ {
if (epdContent[i].compareTo(currentEpdContent[i]) != 0) if (epdContent[i].compareTo(currentEpdContent[i]) != 0)
@ -214,12 +211,12 @@ void taskEpd(void *pvParameters)
} }
} }
#ifdef WITH_RGB_LED #ifdef WITH_RGB_LED
if (updatedThisCycle && preferences.getBool("ledFlashOnUpd", false)) if (updatedThisCycle && preferences.getBool("ledFlashOnUpd", false))
{ {
xTaskNotifyGive(ledHandlerTaskHandle); xTaskNotifyGive(ledHandlerTaskHandle);
} }
#endif #endif
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
} }
@ -346,96 +343,3 @@ void updateDisplay(void *pvParameters)
} }
} }
void showSetupQr(String ssid, String password)
{
int displayIndex = 6;
const String text = "WIFI:S:" + ssid + ";T:WPA;P:" + password + ";;";
uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX];
bool ok = qrcodegen_encodeText(text.c_str(), tempBuffer, qrcode, qrcodegen_Ecc_LOW,
qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true);
const int size = qrcodegen_getSize(qrcode);
const int padding = floor(float(displays[displayIndex].width() - (size * 4)) / 2);
const int paddingY = floor(float(displays[displayIndex].height() - (size * 4)) / 2);
displays[displayIndex].setPartialWindow(0, 0, displays[displayIndex].width(), displays[displayIndex].height());
displays[displayIndex].firstPage();
displays[displayIndex].fillScreen(GxEPD_WHITE);
int border = 0;
do
{
for (int y = -border; y < size * 4 + border; y++)
{
for (int x = -border; x < size * 4 + border; x++)
{
displays[displayIndex].drawPixel(padding + x, paddingY + y, qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4)) ? GxEPD_BLACK : GxEPD_WHITE);
}
}
} while (displays[displayIndex].nextPage());
displayIndex = 4;
displays[displayIndex].setPartialWindow(0, 0, displays[displayIndex].width(), displays[displayIndex].height());
displays[displayIndex].firstPage();
displays[displayIndex].fillScreen(GxEPD_WHITE);
do
{
displays[displayIndex].setTextColor(GxEPD_BLACK);
displays[displayIndex].setCursor(0, 50);
displays[displayIndex].setFont(&FreeSansBold9pt7b);
displays[displayIndex].println("SSID:");
displays[displayIndex].setFont(&FreeSans9pt7b);
displays[displayIndex].println(ssid);
displays[displayIndex].println("");
displays[displayIndex].setFont(&FreeSansBold9pt7b);
displays[displayIndex].println("Password:");
displays[displayIndex].setFont(&FreeSans9pt7b);
displays[displayIndex].println(password);
} while (displays[displayIndex].nextPage());
displayIndex = 2;
displays[displayIndex].setPartialWindow(0, 0, displays[displayIndex].width(), displays[displayIndex].height());
displays[displayIndex].firstPage();
displays[displayIndex].fillScreen(GxEPD_WHITE);
do
{
displays[displayIndex].setTextColor(GxEPD_BLACK);
displays[displayIndex].setCursor(0, 50);
displays[displayIndex].setFont(&FreeSans9pt7b);
displays[displayIndex].println("To setup");
displays[displayIndex].println("scan QR or");
displays[displayIndex].println("connect");
displays[displayIndex].println("manually");
} while (displays[displayIndex].nextPage());
displayIndex = 0;
displays[displayIndex].setPartialWindow(0, 0, displays[displayIndex].width(), displays[displayIndex].height());
displays[displayIndex].firstPage();
displays[displayIndex].fillScreen(GxEPD_WHITE);
do
{
displays[displayIndex].setTextColor(GxEPD_BLACK);
displays[displayIndex].setCursor(0, 50);
displays[displayIndex].setFont(&FreeSansBold9pt7b);
displays[displayIndex].println("Welcome!");
} while (displays[displayIndex].nextPage());
for (int i = 1; i < NUM_SCREENS; (i = i+2)) {
displays[i].setPartialWindow(0, 0, displays[i].width(), displays[i].height());
displays[i].fillScreen(GxEPD_WHITE);
displays[i].display(true);
}
for (int i = 0; i < NUM_SCREENS; i++) {
displays[i].hibernate();
}
}

View File

@ -5,8 +5,7 @@
#else #else
#include <GxEPD2_3C.h> #include <GxEPD2_3C.h>
#endif #endif
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSans9pt7b.h>
#include <string> #include <string>
#include "screens/blockheight.hpp" #include "screens/blockheight.hpp"
@ -17,7 +16,6 @@
#include "screens/custom_text.hpp" #include "screens/custom_text.hpp"
#include "screens/halvingcountdown.hpp" #include "screens/halvingcountdown.hpp"
#include "qrcodegen.h"
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/task.h> #include <freertos/task.h>
@ -47,4 +45,3 @@ void refreshDisplay(void *pvParameters);
void fullRefresh(void *pvParameters); void fullRefresh(void *pvParameters);
void updateDisplay(void *pvParameters); void updateDisplay(void *pvParameters);
//void genQrCode(String text, uint8_t *qrcode[qrcodegen_BUFFER_LEN_MAX]); //void genQrCode(String text, uint8_t *qrcode[qrcodegen_BUFFER_LEN_MAX]);
void showSetupQr(String ssid, String password);

View File

@ -14,29 +14,37 @@ TaskHandle_t getPriceTaskHandle;
void taskGetPrice(void *pvParameters) void taskGetPrice(void *pvParameters)
{ {
IPAddress result;
int err = WiFi.hostByName("api.coingecko.com", result) ;
if (err != 1) {
flashTemporaryLights(255, 255, 0);
}
for (;;) for (;;)
{ {
HTTPClient http; HTTPClient* http = new HTTPClient();
http.setUserAgent(USER_AGENT); http->setUserAgent(USER_AGENT);
if (true) if (true)
{ {
// Send HTTP request to CoinGecko API // Send HTTP request to CoinGecko API
http.begin(cgApiUrl); http->begin(cgApiUrl);
int httpCode = http.GET(); int httpCode = http->GET();
// Parse JSON response and extract average price // Parse JSON response and extract average price
float usdPrice, eurPrice; float usdPrice, eurPrice;
if (httpCode == 200) if (httpCode == 200)
{ {
String payload = http.getString(); String payload = http->getString();
SpiRamJsonDocument doc(768); SpiRamJsonDocument doc(768);
deserializeJson(doc, payload); deserializeJson(doc, payload);
JsonObject bpi = doc["bitcoin"]; JsonObject bpi = doc["bitcoin"];
usdPrice = bpi["usd"]; usdPrice = bpi["usd"];
eurPrice = bpi["eur"]; eurPrice = bpi["eur"];
for (auto &callback : priceEventCallbacks) for (EventCallbackWithNumber &callback : priceEventCallbacks)
{ // Loop through all the event callbacks and call them { // Loop through all the event callbacks and call them
callback(usdPrice); callback(usdPrice);
} }
@ -52,21 +60,21 @@ void taskGetPrice(void *pvParameters)
} else { } else {
// Send HTTP request to CoinDesk API // Send HTTP request to CoinDesk API
http.begin(apiUrl); http->begin(apiUrl);
int httpCode = http.GET(); int httpCode = http->GET();
// Parse JSON response and extract average price // Parse JSON response and extract average price
float usdPrice, eurPrice; float usdPrice, eurPrice;
if (httpCode == 200) if (httpCode == 200)
{ {
String payload = http.getString(); String payload = http->getString();
SpiRamJsonDocument doc(768); SpiRamJsonDocument doc(768);
deserializeJson(doc, payload); deserializeJson(doc, payload);
JsonObject bpi = doc["bpi"]; JsonObject bpi = doc["bpi"];
usdPrice = bpi["USD"]["rate_float"]; usdPrice = bpi["USD"]["rate_float"];
eurPrice = bpi["EUR"]["rate_float"]; eurPrice = bpi["EUR"]["rate_float"];
for (auto &callback : priceEventCallbacks) for (EventCallbackWithNumber &callback : priceEventCallbacks)
{ // Loop through all the event callbacks and call them { // Loop through all the event callbacks and call them
callback(usdPrice); callback(usdPrice);
} }
@ -81,7 +89,9 @@ void taskGetPrice(void *pvParameters)
} }
} }
http.end(); http->end();
delete http;
vTaskDelay(pdMS_TO_TICKS(PRICE_WAIT_TIME)); vTaskDelay(pdMS_TO_TICKS(PRICE_WAIT_TIME));
} }
@ -91,12 +101,12 @@ void setupGetPriceTask()
{ {
if (getPriceTaskHandle == nullptr) if (getPriceTaskHandle == nullptr)
{ {
xTaskCreate(taskGetPrice, "getPrice", 8192, NULL, 1, &getPriceTaskHandle); xTaskCreate(taskGetPrice, "getPrice", 6144, NULL, 1, &getPriceTaskHandle);
vTaskSuspend(getPriceTaskHandle); vTaskSuspend(getPriceTaskHandle);
} }
} }
void registerNewPriceCallback(EventCallbackWithNumber cb) void registerNewPriceCallback(const EventCallbackWithNumber cb)
{ {
priceEventCallbacks.push_back(cb); priceEventCallbacks.push_back(cb);
} }

View File

@ -6,9 +6,10 @@
#include <freertos/task.h> #include <freertos/task.h>
#include "shared.hpp" #include "shared.hpp"
#include "config.h" #include "config.h"
#include "lib/functions.hpp"
extern TaskHandle_t getPriceTaskHandle; extern TaskHandle_t getPriceTaskHandle;
void taskGetPrice(void *pvParameters); void taskGetPrice(void *pvParameters);
void setupGetPriceTask(); void setupGetPriceTask();
void registerNewPriceCallback(EventCallbackWithNumber cb); void registerNewPriceCallback(const EventCallbackWithNumber cb);

View File

@ -38,7 +38,7 @@ void setupMinuteEvent()
xTaskCreate(minuteTask, "MinuteTask", 2048, NULL, 1, &minuteTaskHandle); // Create the FreeRTOS task xTaskCreate(minuteTask, "MinuteTask", 2048, NULL, 1, &minuteTaskHandle); // Create the FreeRTOS task
} }
void registerNewMinuteCallback(EventCallback cb) void registerNewMinuteCallback(const EventCallback cb)
{ {
minuteEventCallbacks.push_back(cb); minuteEventCallbacks.push_back(cb);
} }

View File

@ -11,4 +11,4 @@ extern TaskHandle_t minuteTaskHandle;
void minuteTask(void *pvParameters); void minuteTask(void *pvParameters);
void setupMinuteEvent(); void setupMinuteEvent();
void registerNewMinuteCallback(EventCallback cb); void registerNewMinuteCallback(const EventCallback cb);