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

View file

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

View file

@ -9,10 +9,7 @@ void setupWebserver()
// Initialize SPIFFS
if (!SPIFFS.begin(true))
{
pinMode(47, OUTPUT);
digitalWrite(47, HIGH);
Serial.println("An Error has occurred while mounting SPIFFS");
Serial.println(F("An Error has occurred while mounting SPIFFS"));
return;
}
@ -57,14 +54,14 @@ void setupWebserver()
server.begin();
if (!MDNS.begin(HOSTNAME))
{
Serial.println("Error setting up MDNS responder!");
Serial.println(F("Error setting up MDNS responder!"));
while (1)
{
delay(1000);
}
}
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)
{
timerRunning = false;
Serial.println("Update timer paused");
Serial.println(F("Update timer paused"));
request->send(200);
};
@ -137,7 +134,7 @@ void onApiActionTimerRestart(AsyncWebServerRequest *request)
{
// moment = millis();
timerRunning = true;
Serial.println("Update timer restarted");
Serial.println(F("Update timer restarted"));
request->send(200);
}
@ -221,7 +218,7 @@ bool processEpdColorSettings(AsyncWebServerRequest *request)
AsyncWebParameter *fgColor = request->getParam("fgColor", true);
preferences.putUInt("fgColor", 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());
settingsChanged = true;
}
@ -231,7 +228,7 @@ bool processEpdColorSettings(AsyncWebServerRequest *request)
preferences.putUInt("bgColor", 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());
settingsChanged = true;
}
@ -250,7 +247,7 @@ void onApiEpdSettingsPost(AsyncWebServerRequest *request)
{
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);
Serial.println("Settings changed");
Serial.println(F("Settings changed"));
}
}
@ -504,7 +501,7 @@ void onApiLightsFlash(AsyncWebServerRequest *request)
void onApiLightsSetColor(AsyncWebServerRequest *request)
{
String rgbColor = request->pathArg(0);
int r, g, b;
uint r, g, b;
sscanf(rgbColor.c_str(), "%02x%02x%02x", &r, &g, &b);
setLights(r, g, b);
request->send(200, "text/plain", rgbColor);

View file

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

View file

@ -1,13 +1,23 @@
#include "blockheight.hpp"
uint BlockHeightScreen::blockNr = 0;
std::array<String, NUM_SCREENS> BlockHeightScreen::epdContent = { "", "", "", "", "", "", "" };
//std::array<String, NUM_SCREENS> * BlockHeightScreen::epdContent = (std::array<String, NUM_SCREENS> * ) ps_malloc(7 * sizeof (std::array<String, NUM_SCREENS>));
char **BlockHeightScreen::epdContent;
const int maxStringLength = 15;
char* BlockHeightScreen::psramBuffer;
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);
setupBlockNotify();
BlockHeightScreen::showScreen();
@ -17,10 +27,14 @@ void BlockHeightScreen::showScreen()
{
std::string blockNrString = String(BlockHeightScreen::blockNr).c_str();
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++)
{
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();
}
std::array<String, 7> BlockHeightScreen::getEpdContent() {
return BlockHeightScreen::epdContent;
std::array<String, NUM_SCREENS> BlockHeightScreen::getEpdContent()
{
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,13 +6,15 @@
#include "tasks/epd.hpp"
#include "tasks/blocknotify.hpp"
class BlockHeightScreen {
protected:
static uint blockNr;
static std::array<String, NUM_SCREENS> epdContent;
public:
static void init();
static void showScreen();
static void onNewBlock(uint blockNr);
static std::array<String, NUM_SCREENS> getEpdContent();
class BlockHeightScreen
{
protected:
static uint blockNr;
static char** epdContent;
static char* psramBuffer;
public:
static void init();
static void showScreen();
static void onNewBlock(uint blockNr);
static std::array<String, NUM_SCREENS> getEpdContent();
};

View file

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

View file

@ -3,33 +3,63 @@
uint HalvingCountdownScreen::currentBlockNr = 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()
{
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();
HalvingCountdownScreen::showScreen();
}
void HalvingCountdownScreen::showScreen()
{
if (!initialized)
return;
uint minutesToHalving = HalvingCountdownScreen::getNextHalvingBlockNr() * 10;
int years = floor(minutesToHalving / 525600);
int days = floor((minutesToHalving - (years * 525600)) / (24*60));
int hours = floor((minutesToHalving - (years * 525600) - (days * (24*60))) / 60);
int mins = floor(minutesToHalving - (years * 525600) - (days * (24*60)) - (hours * 60));
// int secs = floor((minutesToHalving - (years * 525600) - (days * (24*60)) - (hours * 60) - mins) * 60);
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));
// int secs = floor((minutesToHalving - (years * 525600) - (days * (24*60)) - (hours * 60) - mins) * 60);
epdContent[0] = F("BIT/COIN");
epdContent[1] = F("HALV/ING");
epdContent[2] = String(years) + "/YRS";
epdContent[3] = String(days) + "/DAYS";
epdContent[4] = String(hours) + "/HRS";
epdContent[5] = String(mins) + "/MINS";
epdContent[6] = F("TO/GO");
snprintf(HalvingCountdownScreen::epdContentP[0], maxStringLength, "BIT/COIN");
snprintf(HalvingCountdownScreen::epdContentP[1], maxStringLength, "HALV/ING");
snprintf(HalvingCountdownScreen::epdContentP[2], maxStringLength, "%d/YRS", years);
snprintf(HalvingCountdownScreen::epdContentP[3], maxStringLength, "%d/DAYS", days);
snprintf(HalvingCountdownScreen::epdContentP[4], maxStringLength, "%d/HRS", hours);
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()
@ -46,5 +76,14 @@ void HalvingCountdownScreen::onNewBlock(uint blockNr)
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,15 +6,21 @@
#include "tasks/epd.hpp"
#include "tasks/blocknotify.hpp"
class HalvingCountdownScreen {
protected:
static uint currentBlockNr;
static uint halvingBlockNr;
static std::array<String, NUM_SCREENS> epdContent;
public:
static void init();
static void showScreen();
static void onNewBlock(uint blockNr);
static uint getNextHalvingBlockNr();
static std::array<String, NUM_SCREENS> getEpdContent();
class HalvingCountdownScreen
{
protected:
static uint currentBlockNr;
static uint halvingBlockNr;
//static std::array<char*, NUM_SCREENS> epdContent;
// static char epdContent[NUM_SCREENS][12];
static char** epdContentP;
// static char **epdContent;
static char *psramBuffer;
public:
static void init();
static void showScreen();
static void onNewBlock(uint blockNr);
static uint getNextHalvingBlockNr();
static std::array<String, NUM_SCREENS> getEpdContent();
};

View file

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

View file

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

View file

@ -1,10 +1,15 @@
#include "ticker.hpp"
uint TickerScreen::price = 12345;
uint TickerScreen::satsPerDollar = 3000;
std::array<String, NUM_SCREENS> TickerScreen::epdContent = { "", "", "", "", "", "", "" };
void TickerScreen::init() {
TickerScreen::price = preferences.getFloat("btcPrice", 12345);;
TickerScreen::satsPerDollar = int(round(1 / preferences.getFloat("btcPrice", 12345) * 10e7));
setupGetPriceTask();
TickerScreen::showScreen();
}
@ -21,9 +26,26 @@ void TickerScreen::showScreen() {
void TickerScreen::onPriceUpdate(uint price) {
TickerScreen::price = price;
TickerScreen::satsPerDollar = int(round(1 / float(price) * 10e7));
TickerScreen::showScreen();
}
std::array<String, NUM_SCREENS> TickerScreen::getEpdContent() {
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:
static uint price;
static std::array<String, NUM_SCREENS> epdContent;
static uint satsPerDollar;
static std::array<String, NUM_SCREENS> epdContent;
public:
static void init();
static void showScreen();
static void onPriceUpdate(uint price);
static std::array<String, NUM_SCREENS> getEpdContent();
static std::array<String, NUM_SCREENS> getEpdContentSats();
};

View file

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

View file

@ -20,11 +20,11 @@ void checkBitcoinBlock(void *pvParameters)
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)
Serial.println("bitcoind node is reachable, using this for blocks.");
Serial.println(F("bitcoind node is reachable, using this for blocks."));
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;
@ -36,53 +36,53 @@ void checkBitcoinBlock(void *pvParameters)
for (;;)
{
HTTPClient http;
http.setUserAgent(USER_AGENT);
HTTPClient *http = new HTTPClient();
http->setUserAgent(USER_AGENT);
if (useBitcoind)
{
StaticJsonDocument<200> jsonDoc;
http.begin(preferences.getString("rpcHost", BITCOIND_HOST).c_str(), preferences.getUInt("rpcPort", BITCOIND_PORT));
http.addHeader("Content-Type", "application/json");
http->begin(preferences.getString("rpcHost", BITCOIND_HOST).c_str(), preferences.getUInt("rpcPort", BITCOIND_PORT));
http->addHeader("Content-Type", "application/json");
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));
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)
{
String response = http.getString();
String response = http->getString();
deserializeJson(jsonDoc, response);
blockHeight = jsonDoc["result"];
}
else
{
Serial.println("Error in HTTP request to bitcoind");
Serial.println(F("Error in HTTP request to bitcoind"));
}
http.end();
http->end();
}
else
{
http.begin("https://" + preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE) + "/api/blocks/tip/height");
int httpCode = http.GET();
http->begin("https://" + preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE) + "/api/blocks/tip/height");
int httpCode = http->GET();
if (httpCode > 0 && httpCode == HTTP_CODE_OK)
{
String blockHeightStr = http.getString();
String blockHeightStr = http->getString();
blockHeight = blockHeightStr.toInt();
}
else
{
Serial.print(F("Error in HTTP request to mempool API: "));
Serial.print(httpCode);
Serial.println(http.errorToString(httpCode));
Serial.println(http->errorToString(httpCode));
}
http.end();
http->end();
}
if (blockHeight > currentBlockHeight)
{
@ -93,7 +93,7 @@ void checkBitcoinBlock(void *pvParameters)
currentBlockHeight = blockHeight;
preferences.putUInt("blockHeight", currentBlockHeight);
}
delete http;
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);
}
void registerNewBlockCallback(EventCallbackWithNumber cb)
void registerNewBlockCallback(const EventCallbackWithNumber cb)
{
blockEventCallbacks.push_back(cb);
}

View file

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

View file

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

View file

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

View file

@ -1,17 +1,12 @@
#include "epd.hpp"
#ifdef IS_S3
// reversed
// const int EPD_CS[7] = {17, 21, 33, 10, 6, 4, 2};
// const int EPD_BUSY[7] = {16, 18, 37, 9, 7, 5, 3};
// const int EPD_RESET_MPD[7] = {14, 13, 12, 11, 10, 9, 8};
const char EPD_CS[NUM_SCREENS] = {2, 4, 6, 10, 33, 21, 17};
const char EPD_BUSY[NUM_SCREENS] = {3, 5, 7, 9, 37, 18, 16};
const char EPD_RESET_MPD[NUM_SCREENS] = {8, 9, 10, 11, 12, 13, 14};
const int EPD_CS[NUM_SCREENS] = {2, 4, 6, 10, 33, 21, 17};
const int EPD_BUSY[NUM_SCREENS] = {3, 5, 7, 9, 37, 18, 16};
const int EPD_RESET_MPD[NUM_SCREENS] = {8, 9, 10, 11, 12, 13, 14};
const int EPD_DC = 14;
const int RST_PIN = 15;
const char EPD_DC = 14;
const char RST_PIN = 15;
#elif defined(IS_S2)
// 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_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;
@ -81,7 +76,10 @@ std::array<String, 7> currentEpdContent;
std::array<String, 7> epdContent;
TaskHandle_t tasks[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()
{
@ -91,7 +89,7 @@ void setupDisplays()
void resetAllDisplays()
{
#ifdef NO_MCP
#ifdef NO_MCP
digitalWrite(RST_PIN, HIGH);
pinMode(RST_PIN, OUTPUT);
delay(20);
@ -99,23 +97,24 @@ void resetAllDisplays()
delay(20);
digitalWrite(RST_PIN, HIGH);
delay(200);
#else
for (int i = 0; i < NUM_SCREENS; i++) {
#else
for (int i = 0; i < NUM_SCREENS; i++)
{
resetSingleDisplay(i);
}
#endif NO_MCP
#endif
}
void resetSingleDisplay(int i)
{
#ifndef NO_MCP
#ifndef NO_MCP
mcp.digitalWrite(EPD_RESET_MPD[i], HIGH);
delay(20);
mcp.digitalWrite(EPD_RESET_MPD[i], LOW);
delay(20);
mcp.digitalWrite(EPD_RESET_MPD[i], HIGH);
delay(200);
#endif
#endif
}
void initDisplays()
@ -166,7 +165,7 @@ void taskEpd(void *pvParameters)
epdContent = TickerScreen::getEpdContent();
break;
case SCREEN_MSCW_TIME:
epdContent = SatsPerDollarScreen::getEpdContent();
epdContent = TickerScreen::getEpdContentSats();
break;
case SCREEN_TIME:
epdContent = TimeScreen::getEpdContent();
@ -182,10 +181,8 @@ void taskEpd(void *pvParameters)
break;
}
bool updatedThisCycle = false;
for (uint i = 0; i < NUM_SCREENS; i++)
{
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))
{
xTaskNotifyGive(ledHandlerTaskHandle);
}
#endif
#endif
vTaskDelay(pdMS_TO_TICKS(1000));
}
@ -321,7 +318,7 @@ void updateDisplay(void *pvParameters)
bool updatePartial = true;
// Full Refresh every half hour
if (!lastFullRefresh[epdIndex] || (millis() - lastFullRefresh[epdIndex]) > (preferences.getUInt("fullRefreshMin", 30) * 60 * 1000))
if (!lastFullRefresh[epdIndex] || (millis() - lastFullRefresh[epdIndex]) > (preferences.getUInt("fullRefreshMin", 30) * 60 * 1000))
{
updatePartial = false;
lastFullRefresh[epdIndex] = millis();
@ -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
#include <GxEPD2_3C.h>
#endif
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSans9pt7b.h>
#include <string>
#include "screens/blockheight.hpp"
@ -17,7 +16,6 @@
#include "screens/custom_text.hpp"
#include "screens/halvingcountdown.hpp"
#include "qrcodegen.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
@ -47,4 +45,3 @@ void refreshDisplay(void *pvParameters);
void fullRefresh(void *pvParameters);
void updateDisplay(void *pvParameters);
//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)
{
IPAddress result;
int err = WiFi.hostByName("api.coingecko.com", result) ;
if (err != 1) {
flashTemporaryLights(255, 255, 0);
}
for (;;)
{
HTTPClient http;
http.setUserAgent(USER_AGENT);
HTTPClient* http = new HTTPClient();
http->setUserAgent(USER_AGENT);
if (true)
{
// 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
float usdPrice, eurPrice;
if (httpCode == 200)
{
String payload = http.getString();
String payload = http->getString();
SpiRamJsonDocument doc(768);
deserializeJson(doc, payload);
JsonObject bpi = doc["bitcoin"];
usdPrice = bpi["usd"];
eurPrice = bpi["eur"];
for (auto &callback : priceEventCallbacks)
for (EventCallbackWithNumber &callback : priceEventCallbacks)
{ // Loop through all the event callbacks and call them
callback(usdPrice);
}
@ -52,21 +60,21 @@ void taskGetPrice(void *pvParameters)
} else {
// 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
float usdPrice, eurPrice;
if (httpCode == 200)
{
String payload = http.getString();
String payload = http->getString();
SpiRamJsonDocument doc(768);
deserializeJson(doc, payload);
JsonObject bpi = doc["bpi"];
usdPrice = bpi["USD"]["rate_float"];
eurPrice = bpi["EUR"]["rate_float"];
for (auto &callback : priceEventCallbacks)
for (EventCallbackWithNumber &callback : priceEventCallbacks)
{ // Loop through all the event callbacks and call them
callback(usdPrice);
}
@ -81,7 +89,9 @@ void taskGetPrice(void *pvParameters)
}
}
http.end();
http->end();
delete http;
vTaskDelay(pdMS_TO_TICKS(PRICE_WAIT_TIME));
}
@ -91,12 +101,12 @@ void setupGetPriceTask()
{
if (getPriceTaskHandle == nullptr)
{
xTaskCreate(taskGetPrice, "getPrice", 8192, NULL, 1, &getPriceTaskHandle);
xTaskCreate(taskGetPrice, "getPrice", 6144, NULL, 1, &getPriceTaskHandle);
vTaskSuspend(getPriceTaskHandle);
}
}
void registerNewPriceCallback(EventCallbackWithNumber cb)
void registerNewPriceCallback(const EventCallbackWithNumber cb)
{
priceEventCallbacks.push_back(cb);
}

View file

@ -6,9 +6,10 @@
#include <freertos/task.h>
#include "shared.hpp"
#include "config.h"
#include "lib/functions.hpp"
extern TaskHandle_t getPriceTaskHandle;
void taskGetPrice(void *pvParameters);
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
}
void registerNewMinuteCallback(EventCallback cb)
void registerNewMinuteCallback(const EventCallback cb)
{
minuteEventCallbacks.push_back(cb);
}

View file

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