Use clang to make codestyle consistent

This commit is contained in:
Djuri Baars 2023-11-30 22:38:01 +01:00
parent 239415c7aa
commit 4aab02a040
31 changed files with 10588 additions and 10871 deletions

View File

@ -1,3 +1,6 @@
#include <Adafruit_GFX.h>
#include <Arduino.h>
const uint8_t Antonio_SemiBold20pt7bBitmaps[] PROGMEM = { const uint8_t Antonio_SemiBold20pt7bBitmaps[] PROGMEM = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x66, 0x66, 0x66, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x66, 0x66, 0x66,
0x66, 0x66, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7B, 0xDE, 0xF7, 0x66, 0x66, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7B, 0xDE, 0xF7,
@ -476,7 +479,6 @@ const GFXglyph Antonio_SemiBold20pt7bGlyphs[] PROGMEM = {
const GFXfont Antonio_SemiBold20pt7b PROGMEM = { const GFXfont Antonio_SemiBold20pt7b PROGMEM = {
(uint8_t *)Antonio_SemiBold20pt7bBitmaps, (uint8_t *)Antonio_SemiBold20pt7bBitmaps,
(GFXglyph *)Antonio_SemiBold20pt7bGlyphs, (GFXglyph *)Antonio_SemiBold20pt7bGlyphs, 0x20, 0x7E, 51};
0x20, 0x7E, 51 };
// Approx. 5193 bytes // Approx. 5193 bytes

View File

@ -1,3 +1,6 @@
#include <Adafruit_GFX.h>
#include <Arduino.h>
const uint8_t Antonio_SemiBold30pt7bBitmaps[] PROGMEM = { const uint8_t Antonio_SemiBold30pt7bBitmaps[] PROGMEM = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xE7, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xE7,
0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9,
@ -948,7 +951,6 @@ const GFXglyph Antonio_SemiBold30pt7bGlyphs[] PROGMEM = {
const GFXfont Antonio_SemiBold30pt7b PROGMEM = { const GFXfont Antonio_SemiBold30pt7b PROGMEM = {
(uint8_t *)Antonio_SemiBold30pt7bBitmaps, (uint8_t *)Antonio_SemiBold30pt7bBitmaps,
(GFXglyph *)Antonio_SemiBold30pt7bGlyphs, (GFXglyph *)Antonio_SemiBold30pt7bGlyphs, 0x20, 0x7E, 76};
0x20, 0x7E, 76 };
// Approx. 10860 bytes // Approx. 10860 bytes

View File

@ -1,3 +1,6 @@
#include <Adafruit_GFX.h>
#include <Arduino.h>
const uint8_t Antonio_SemiBold40pt7bBitmaps[] PROGMEM = { const uint8_t Antonio_SemiBold40pt7bBitmaps[] PROGMEM = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFE, 0x7F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFE, 0x7F,
0x9F, 0xE7, 0xF9, 0xFE, 0x7F, 0x9F, 0xE7, 0xF9, 0xFE, 0x7F, 0x9F, 0xE7, 0x9F, 0xE7, 0xF9, 0xFE, 0x7F, 0x9F, 0xE7, 0xF9, 0xFE, 0x7F, 0x9F, 0xE7,
@ -1624,7 +1627,6 @@ const GFXglyph Antonio_SemiBold40pt7bGlyphs[] PROGMEM = {
const GFXfont Antonio_SemiBold40pt7b PROGMEM = { const GFXfont Antonio_SemiBold40pt7b PROGMEM = {
(uint8_t *)Antonio_SemiBold40pt7bBitmaps, (uint8_t *)Antonio_SemiBold40pt7bBitmaps,
(GFXglyph *)Antonio_SemiBold40pt7bGlyphs, (GFXglyph *)Antonio_SemiBold40pt7bGlyphs, 0x20, 0x7E, 101};
0x20, 0x7E, 101 };
// Approx. 18961 bytes // Approx. 18961 bytes

View File

@ -1,3 +1,6 @@
#include <Adafruit_GFX.h>
#include <Arduino.h>
const uint8_t Antonio_SemiBold90pt7bBitmaps[] PROGMEM = { const uint8_t Antonio_SemiBold90pt7bBitmaps[] PROGMEM = {
0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF,
0x7F, 0xFF, 0xFD, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0x7F, 0x7F, 0xFF, 0xFD, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0x7F,
@ -5087,11 +5090,11 @@ const GFXglyph Antonio_SemiBold90pt7bGlyphs[] PROGMEM = {
{53398, 108, 151, 116, 4, 106}, // 0x57 'W' {53398, 108, 151, 116, 4, 106}, // 0x57 'W'
{55437, 64, 151, 72, 4, 106}, // 0x58 'X' {55437, 64, 151, 72, 4, 106}, // 0x58 'X'
{56645, 71, 151, 73, 1, 106}, // 0x59 'Y' {56645, 71, 151, 73, 1, 106}, // 0x59 'Y'
{ 57986, 52, 151, 61, 6, 106 }, { 58968, 70, 155, 84, 5, 104 } }; // 0x5A 'Z' {57986, 52, 151, 61, 6, 106},
{58968, 70, 155, 84, 5, 104}}; // 0x5A 'Z'
const GFXfont Antonio_SemiBold90pt7b PROGMEM = { const GFXfont Antonio_SemiBold90pt7b PROGMEM = {
(uint8_t *)Antonio_SemiBold90pt7bBitmaps, (uint8_t *)Antonio_SemiBold90pt7bBitmaps,
(GFXglyph *)Antonio_SemiBold90pt7bGlyphs, (GFXglyph *)Antonio_SemiBold90pt7bGlyphs, 0x20, 0x5B, 228};
0x20, 0x5B, 228 };
// Approx. 60745 bytes // Approx. 60745 bytes

View File

@ -48,21 +48,19 @@ uint currentBlockHeight = 816000;
// ew== // ew==
// -----END CERTIFICATE-----)"; // -----END CERTIFICATE-----)";
void setupBlockNotify() void setupBlockNotify() {
{
// currentBlockHeight = preferences.getUInt("blockHeight", 816000); // currentBlockHeight = preferences.getUInt("blockHeight", 816000);
IPAddress result; IPAddress result;
int dnsErr = -1; int dnsErr = -1;
String mempoolInstance = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE); String mempoolInstance =
preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
while (dnsErr != 1) while (dnsErr != 1) {
{
dnsErr = WiFi.hostByName(mempoolInstance.c_str(), result); dnsErr = WiFi.hostByName(mempoolInstance.c_str(), result);
if (dnsErr != 1) if (dnsErr != 1) {
{
Serial.print(mempoolInstance); Serial.print(mempoolInstance);
Serial.println(F("mempool DNS could not be resolved")); Serial.println(F("mempool DNS could not be resolved"));
WiFi.reconnect(); WiFi.reconnect();
@ -75,19 +73,18 @@ void setupBlockNotify()
http->begin("https://" + mempoolInstance + "/api/blocks/tip/height"); http->begin("https://" + mempoolInstance + "/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();
currentBlockHeight = blockHeightStr.toInt(); currentBlockHeight = blockHeightStr.toInt();
// xTaskNotifyGive(blockUpdateTaskHandle); // xTaskNotifyGive(blockUpdateTaskHandle);
if (workQueue != nullptr) if (workQueue != nullptr) {
{
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0}; WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
} }
} }
// std::strcpy(wsServer, String("wss://" + mempoolInstance + "/api/v1/ws").c_str()); // std::strcpy(wsServer, String("wss://" + mempoolInstance +
// "/api/v1/ws").c_str());
esp_websocket_client_config_t config = { esp_websocket_client_config_t config = {
.uri = "wss://mempool.space/api/v1/ws", .uri = "wss://mempool.space/api/v1/ws",
@ -97,22 +94,22 @@ void setupBlockNotify()
}; };
blockNotifyClient = esp_websocket_client_init(&config); blockNotifyClient = esp_websocket_client_init(&config);
esp_websocket_register_events(blockNotifyClient, WEBSOCKET_EVENT_ANY, onWebsocketEvent, blockNotifyClient); esp_websocket_register_events(blockNotifyClient, WEBSOCKET_EVENT_ANY,
onWebsocketEvent, blockNotifyClient);
esp_websocket_client_start(blockNotifyClient); esp_websocket_client_start(blockNotifyClient);
} }
void onWebsocketEvent(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) void onWebsocketEvent(void *handler_args, esp_event_base_t base,
{ int32_t event_id, void *event_data) {
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data; esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
const String sub = "{\"action\": \"want\", \"data\":[\"blocks\"]}"; const String sub = "{\"action\": \"want\", \"data\":[\"blocks\"]}";
switch (event_id) switch (event_id) {
{
case WEBSOCKET_EVENT_CONNECTED: case WEBSOCKET_EVENT_CONNECTED:
Serial.println(F("Connected to Mempool.space WebSocket")); Serial.println(F("Connected to Mempool.space WebSocket"));
Serial.println(sub); Serial.println(sub);
if (esp_websocket_client_send_text(blockNotifyClient, sub.c_str(), sub.length(), portMAX_DELAY) == -1) if (esp_websocket_client_send_text(blockNotifyClient, sub.c_str(),
{ sub.length(), portMAX_DELAY) == -1) {
Serial.println(F("Mempool.space WS Block Subscribe Error")); Serial.println(F("Mempool.space WS Block Subscribe Error"));
} }
@ -129,14 +126,12 @@ void onWebsocketEvent(void *handler_args, esp_event_base_t base, int32_t event_i
} }
} }
void onWebsocketMessage(esp_websocket_event_data_t *event_data) void onWebsocketMessage(esp_websocket_event_data_t *event_data) {
{
SpiRamJsonDocument doc(event_data->data_len); SpiRamJsonDocument doc(event_data->data_len);
deserializeJson(doc, (char *)event_data->data_ptr); deserializeJson(doc, (char *)event_data->data_ptr);
if (doc.containsKey("block")) if (doc.containsKey("block")) {
{
JsonObject block = doc["block"]; JsonObject block = doc["block"];
currentBlockHeight = block["height"].as<uint>(); currentBlockHeight = block["height"].as<uint>();
@ -144,14 +139,13 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data)
Serial.printf("New block found: %d\r\n", block["height"].as<uint>()); Serial.printf("New block found: %d\r\n", block["height"].as<uint>());
preferences.putUInt("blockHeight", currentBlockHeight); preferences.putUInt("blockHeight", currentBlockHeight);
if (workQueue != nullptr) if (workQueue != nullptr) {
{
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0}; WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
// xTaskNotifyGive(blockUpdateTaskHandle); // xTaskNotifyGive(blockUpdateTaskHandle);
if (getCurrentScreen() != SCREEN_BLOCK_HEIGHT && preferences.getBool("stealFocus", true)) if (getCurrentScreen() != SCREEN_BLOCK_HEIGHT &&
{ preferences.getBool("stealFocus", true)) {
uint64_t timerPeriod = 0; uint64_t timerPeriod = 0;
if (isTimerActive()) { if (isTimerActive()) {
// store timer periode before making inactive to prevent artifacts // store timer periode before making inactive to prevent artifacts
@ -160,12 +154,13 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data)
} }
setCurrentScreen(SCREEN_BLOCK_HEIGHT); setCurrentScreen(SCREEN_BLOCK_HEIGHT);
if (timerPeriod > 0) { if (timerPeriod > 0) {
esp_timer_start_periodic(screenRotateTimer, timerPeriod * usPerSecond); esp_timer_start_periodic(screenRotateTimer,
timerPeriod * usPerSecond);
} }
} }
if (getCurrentScreen() == SCREEN_BLOCK_HEIGHT && preferences.getBool("ledFlashOnUpd", false)) if (getCurrentScreen() == SCREEN_BLOCK_HEIGHT &&
{ preferences.getBool("ledFlashOnUpd", false)) {
vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated
queueLedEffect(LED_FLASH_BLOCK_NOTIFY); queueLedEffect(LED_FLASH_BLOCK_NOTIFY);
} }
@ -175,25 +170,19 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data)
doc.clear(); doc.clear();
} }
uint getBlockHeight() uint getBlockHeight() { return currentBlockHeight; }
{
return currentBlockHeight;
}
void setBlockHeight(uint newBlockHeight) void setBlockHeight(uint newBlockHeight) {
{
currentBlockHeight = newBlockHeight; currentBlockHeight = newBlockHeight;
} }
bool isBlockNotifyConnected() bool isBlockNotifyConnected() {
{
if (blockNotifyClient == NULL) if (blockNotifyClient == NULL)
return false; return false;
return esp_websocket_client_is_connected(blockNotifyClient); return esp_websocket_client_is_connected(blockNotifyClient);
} }
void stopBlockNotify() void stopBlockNotify() {
{
esp_websocket_client_stop(blockNotifyClient); esp_websocket_client_stop(blockNotifyClient);
esp_websocket_client_destroy(blockNotifyClient); esp_websocket_client_destroy(blockNotifyClient);
} }

View File

@ -1,21 +1,22 @@
#pragma once #pragma once
#include <cstring> #include "lib/led_handler.hpp"
#include <string> #include "lib/screen_handler.hpp"
#include "lib/shared.hpp"
#include <Arduino.h> #include <Arduino.h>
#include <HTTPClient.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include "shared.hpp" #include <HTTPClient.h>
#include "esp_timer.h" #include <cstring>
#include "esp_websocket_client.h" #include <esp_timer.h>
#include "screen_handler.hpp" #include <esp_websocket_client.h>
#include "led_handler.hpp" #include <string>
// using namespace websockets; // using namespace websockets;
void setupBlockNotify(); void setupBlockNotify();
void onWebsocketEvent(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data); void onWebsocketEvent(void *handler_args, esp_event_base_t base,
int32_t event_id, void *event_data);
void onWebsocketMessage(esp_websocket_event_data_t *event_data); void onWebsocketMessage(esp_websocket_event_data_t *event_data);
void setBlockHeight(uint newBlockHeight); void setBlockHeight(uint newBlockHeight);

View File

@ -4,24 +4,19 @@ TaskHandle_t buttonTaskHandle = NULL;
const TickType_t debounceDelay = pdMS_TO_TICKS(50); const TickType_t debounceDelay = pdMS_TO_TICKS(50);
TickType_t lastDebounceTime = 0; TickType_t lastDebounceTime = 0;
void buttonTask(void *parameter) void buttonTask(void *parameter) {
{ while (1) {
while (1)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
std::lock_guard<std::mutex> lock(mcpMutex); std::lock_guard<std::mutex> lock(mcpMutex);
TickType_t currentTime = xTaskGetTickCount(); TickType_t currentTime = xTaskGetTickCount();
if ((currentTime - lastDebounceTime) >= debounceDelay) if ((currentTime - lastDebounceTime) >= debounceDelay) {
{
lastDebounceTime = currentTime; lastDebounceTime = currentTime;
if (!digitalRead(MCP_INT_PIN)) if (!digitalRead(MCP_INT_PIN)) {
{
uint pin = mcp1.getLastInterruptPin(); uint pin = mcp1.getLastInterruptPin();
switch (pin) switch (pin) {
{
case 3: case 3:
toggleTimerActive(); toggleTimerActive();
break; break;
@ -37,31 +32,26 @@ void buttonTask(void *parameter)
} }
} }
mcp1.clearInterrupts(); mcp1.clearInterrupts();
} } else {
else
{
} }
// Very ugly, but for some reason this is necessary // Very ugly, but for some reason this is necessary
while (!digitalRead(MCP_INT_PIN)) while (!digitalRead(MCP_INT_PIN)) {
{
mcp1.clearInterrupts(); mcp1.clearInterrupts();
} }
} }
} }
void IRAM_ATTR handleButtonInterrupt() void IRAM_ATTR handleButtonInterrupt() {
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(buttonTaskHandle, 0, eNoAction, &xHigherPriorityTaskWoken); xTaskNotifyFromISR(buttonTaskHandle, 0, eNoAction, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE) if (xHigherPriorityTaskWoken == pdTRUE) {
{
portYIELD_FROM_ISR(); portYIELD_FROM_ISR();
} }
} }
void setupButtonTask() void setupButtonTask() {
{ xTaskCreate(buttonTask, "ButtonTask", 4096, NULL, tskIDLE_PRIORITY,
xTaskCreate(buttonTask, "ButtonTask", 4096, NULL, tskIDLE_PRIORITY, &buttonTaskHandle); // Create the FreeRTOS task &buttonTaskHandle); // Create the FreeRTOS task
// Use interrupt instead of task // Use interrupt instead of task
attachInterrupt(MCP_INT_PIN, handleButtonInterrupt, CHANGE); attachInterrupt(MCP_INT_PIN, handleButtonInterrupt, CHANGE);
} }

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "lib/screen_handler.hpp"
#include "lib/shared.hpp"
#include <Arduino.h> #include <Arduino.h>
#include "shared.hpp"
#include "screen_handler.hpp"
extern TaskHandle_t buttonTaskHandle; extern TaskHandle_t buttonTaskHandle;

View File

@ -10,19 +10,16 @@ Adafruit_MCP23X17 mcp2;
std::vector<std::string> screenNameMap(SCREEN_COUNT); std::vector<std::string> screenNameMap(SCREEN_COUNT);
std::mutex mcpMutex; std::mutex mcpMutex;
void setup() void setup() {
{
setupPreferences(); setupPreferences();
setupHardware(); setupHardware();
setupDisplays(); setupDisplays();
if (preferences.getBool("ledTestOnPower", true)) if (preferences.getBool("ledTestOnPower", true)) {
{
queueLedEffect(LED_POWER_TEST); queueLedEffect(LED_POWER_TEST);
} }
{ {
std::lock_guard<std::mutex> lockMcp(mcpMutex); std::lock_guard<std::mutex> lockMcp(mcpMutex);
if (mcp1.digitalRead(3) == LOW) if (mcp1.digitalRead(3) == LOW) {
{
preferences.putBool("wifiConfigured", false); preferences.putBool("wifiConfigured", false);
preferences.remove("txPower"); preferences.remove("txPower");
@ -42,7 +39,8 @@ void setup()
setupTasks(); setupTasks();
setupTimers(); setupTimers();
xTaskCreate(setupWebsocketClients, "setupWebsocketClients", 4096, NULL, tskIDLE_PRIORITY, NULL); xTaskCreate(setupWebsocketClients, "setupWebsocketClients", 4096, NULL,
tskIDLE_PRIORITY, NULL);
setupButtonTask(); setupButtonTask();
setupOTA(); setupOTA();
@ -51,13 +49,10 @@ void setup()
forceFullRefresh(); forceFullRefresh();
} }
void tryImprovSetup() void tryImprovSetup() {
{
WiFi.onEvent(WiFiEvent); WiFi.onEvent(WiFiEvent);
if (!preferences.getBool("wifiConfigured", false)) {
if (!preferences.getBool("wifiConfigured", false))
{
setFgColor(GxEPD_BLACK); setFgColor(GxEPD_BLACK);
setBgColor(GxEPD_WHITE); setBgColor(GxEPD_WHITE);
queueLedEffect(LED_EFFECT_WIFI_WAIT_FOR_CONFIG); queueLedEffect(LED_EFFECT_WIFI_WAIT_FOR_CONFIG);
@ -76,42 +71,59 @@ void tryImprovSetup()
byte mac[6]; byte mac[6];
WiFi.macAddress(mac); WiFi.macAddress(mac);
String softAP_SSID = String("BTClock" + String(mac[5], 16) + String(mac[1], 16)); String softAP_SSID =
String("BTClock" + String(mac[5], 16) + String(mac[1], 16));
WiFi.setHostname(softAP_SSID.c_str()); WiFi.setHostname(softAP_SSID.c_str());
String softAP_password = base64::encode(String(mac[2], 16) + String(mac[4], 16) + String(mac[5], 16) + String(mac[1], 16)).substring(2, 10); String softAP_password =
base64::encode(String(mac[2], 16) + String(mac[4], 16) +
String(mac[5], 16) + String(mac[1], 16))
.substring(2, 10);
// wm.setConfigPortalTimeout(preferences.getUInt("wpTimeout", 600)); // wm.setConfigPortalTimeout(preferences.getUInt("wpTimeout", 600));
wm.setWiFiAutoReconnect(false); wm.setWiFiAutoReconnect(false);
wm.setDebugOutput(false); wm.setDebugOutput(false);
wm.setConfigPortalBlocking(true); wm.setConfigPortalBlocking(true);
wm.setAPCallback([&](WiFiManager *wifiManager) wm.setAPCallback([&](WiFiManager *wifiManager) {
{
// Serial.printf("Entered config mode:ip=%s, ssid='%s', pass='%s'\n", // Serial.printf("Entered config mode:ip=%s, ssid='%s', pass='%s'\n",
// WiFi.softAPIP().toString().c_str(), // WiFi.softAPIP().toString().c_str(),
// wifiManager->getConfigPortalSSID().c_str(), // wifiManager->getConfigPortalSSID().c_str(),
// softAP_password.c_str()); // softAP_password.c_str());
// delay(6000); // delay(6000);
const String qrText = "qrWIFI:S:" + wifiManager->getConfigPortalSSID() + ";T:WPA;P:" + softAP_password.c_str() + ";;"; const String qrText = "qrWIFI:S:" + wifiManager->getConfigPortalSSID() +
const String explainText = "*SSID: *\r\n" + wifiManager->getConfigPortalSSID() + "\r\n\r\n*Password:*\r\n" + softAP_password; ";T:WPA;P:" + softAP_password.c_str() + ";;";
std::array<String, NUM_SCREENS> epdContent = {"Welcome!", "Bienvenidos!", "To setup\r\nscan QR or\r\nconnect\r\nmanually", "Para\r\nconfigurar\r\nescanear QR\r\no conectar\r\nmanualmente", explainText, " ", qrText}; const String explainText = "*SSID: *\r\n" +
setEpdContent(epdContent); }); wifiManager->getConfigPortalSSID() +
"\r\n\r\n*Password:*\r\n" + softAP_password;
std::array<String, NUM_SCREENS> epdContent = {
"Welcome!",
"Bienvenidos!",
"To setup\r\nscan QR or\r\nconnect\r\nmanually",
"Para\r\nconfigurar\r\nescanear QR\r\no conectar\r\nmanualmente",
explainText,
" ",
qrText};
setEpdContent(epdContent);
});
wm.setSaveConfigCallback([]() wm.setSaveConfigCallback([]() {
{
preferences.putBool("wifiConfigured", true); preferences.putBool("wifiConfigured", true);
delay(1000); delay(1000);
// just restart after succes // just restart after succes
ESP.restart(); }); ESP.restart();
});
bool ac = wm.autoConnect(softAP_SSID.c_str(), softAP_password.c_str()); bool ac = wm.autoConnect(softAP_SSID.c_str(), softAP_password.c_str());
// waitUntilNoneBusy(); // waitUntilNoneBusy();
//std::array<String, NUM_SCREENS> epdContent = {"Welcome!", "Bienvenidos!", "Use\r\nweb-interface\r\nto configure", "Use\r\nla interfaz web\r\npara configurar", "Or restart\r\nwhile\r\nholding\r\n2nd button\r\r\nto start\r\n QR-config", "O reinicie\r\nmientras\r\n mantiene presionado\r\nel segundo botón\r\r\npara iniciar\r\nQR-config", ""}; // std::array<String, NUM_SCREENS> epdContent = {"Welcome!",
//setEpdContent(epdContent); // "Bienvenidos!", "Use\r\nweb-interface\r\nto configure", "Use\r\nla
// interfaz web\r\npara configurar", "Or
// restart\r\nwhile\r\nholding\r\n2nd button\r\r\nto start\r\n QR-config",
// "O reinicie\r\nmientras\r\n mantiene presionado\r\nel segundo
// botón\r\r\npara iniciar\r\nQR-config", ""}; setEpdContent(epdContent);
// esp_task_wdt_init(30, false); // esp_task_wdt_init(30, false);
// uint count = 0; // uint count = 0;
// while (WiFi.status() != WL_CONNECTED) // while (WiFi.status() != WL_CONNECTED)
@ -120,7 +132,8 @@ void tryImprovSetup()
// { // {
// uint8_t b = Serial.read(); // uint8_t b = Serial.read();
// if (parse_improv_serial_byte(x_position, b, x_buffer, onImprovCommandCallback, onImprovErrorCallback)) // if (parse_improv_serial_byte(x_position, b, x_buffer,
// onImprovCommandCallback, onImprovErrorCallback))
// { // {
// x_buffer[x_position++] = b; // x_buffer[x_position++] = b;
// } // }
@ -138,46 +151,42 @@ void tryImprovSetup()
// } // }
// esp_task_wdt_deinit(); // esp_task_wdt_deinit();
// esp_task_wdt_reset(); // esp_task_wdt_reset();
} }
setFgColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR)); setFgColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR));
setBgColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR)); setBgColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR));
} } else {
else
{
WiFi.setAutoConnect(true); WiFi.setAutoConnect(true);
WiFi.setAutoReconnect(true); WiFi.setAutoReconnect(true);
WiFi.begin(); WiFi.begin();
if (preferences.getInt("txPower", 0)) { if (preferences.getInt("txPower", 0)) {
if(WiFi.setTxPower(static_cast<wifi_power_t>(preferences.getInt("txPower", 0)))) { if (WiFi.setTxPower(
Serial.printf("WiFi max tx power set to %d\n", preferences.getInt("txPower", 0)); static_cast<wifi_power_t>(preferences.getInt("txPower", 0)))) {
Serial.printf("WiFi max tx power set to %d\n",
preferences.getInt("txPower", 0));
} }
} }
while (WiFi.status() != WL_CONNECTED) {
while (WiFi.status() != WL_CONNECTED)
{
vTaskDelay(pdMS_TO_TICKS(400)); vTaskDelay(pdMS_TO_TICKS(400));
} }
} }
// queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS); // queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS);
} }
void setupTime() void setupTime() {
{ configTime(preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS), 0,
configTime(preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS), 0, NTP_SERVER); NTP_SERVER);
struct tm timeinfo; struct tm timeinfo;
while (!getLocalTime(&timeinfo)) while (!getLocalTime(&timeinfo)) {
{ configTime(preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS), 0,
configTime(preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS), 0, NTP_SERVER); NTP_SERVER);
delay(500); delay(500);
Serial.println(F("Retry set time")); Serial.println(F("Retry set time"));
} }
} }
void setupPreferences() void setupPreferences() {
{
preferences.begin("btclock", false); preferences.begin("btclock", false);
setFgColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR)); setFgColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR));
@ -193,49 +202,37 @@ void setupPreferences()
screenNameMap[SCREEN_MARKET_CAP] = "Market Cap"; screenNameMap[SCREEN_MARKET_CAP] = "Market Cap";
} }
void setupWebsocketClients(void *pvParameters) void setupWebsocketClients(void *pvParameters) {
{
setupBlockNotify(); setupBlockNotify();
if (preferences.getBool("fetchEurPrice", false)) if (preferences.getBool("fetchEurPrice", false)) {
{
setupPriceFetchTask(); setupPriceFetchTask();
} } else {
else
{
setupPriceNotify(); setupPriceNotify();
} }
vTaskDelete(NULL); vTaskDelete(NULL);
} }
void setupTimers() void setupTimers() {
{ xTaskCreate(setupTimeUpdateTimer, "setupTimeUpdateTimer", 2048, NULL,
xTaskCreate(setupTimeUpdateTimer, "setupTimeUpdateTimer", 2048, NULL, tskIDLE_PRIORITY, NULL); tskIDLE_PRIORITY, NULL);
xTaskCreate(setupScreenRotateTimer, "setupScreenRotateTimer", 2048, NULL, tskIDLE_PRIORITY, NULL); xTaskCreate(setupScreenRotateTimer, "setupScreenRotateTimer", 2048, NULL,
tskIDLE_PRIORITY, NULL);
} }
void finishSetup() void finishSetup() {
{
if (preferences.getBool("ledStatus", false)) if (preferences.getBool("ledStatus", false)) {
{
restoreLedState(); restoreLedState();
} } else {
else
{
clearLeds(); clearLeds();
} }
} }
std::vector<std::string> getScreenNameMap() std::vector<std::string> getScreenNameMap() { return screenNameMap; }
{
return screenNameMap;
}
void setupMcp() {
void setupMcp()
{
#ifdef IS_BTCLOCK_S3 #ifdef IS_BTCLOCK_S3
const int mcp1AddrPins[] = {MCP1_A0_PIN, MCP1_A1_PIN, MCP1_A2_PIN}; const int mcp1AddrPins[] = {MCP1_A0_PIN, MCP1_A1_PIN, MCP1_A2_PIN};
const int mcp1AddrValues[] = {LOW, LOW, LOW}; const int mcp1AddrValues[] = {LOW, LOW, LOW};
@ -260,10 +257,8 @@ void setupMcp()
#endif #endif
} }
void setupHardware() void setupHardware() {
{ if (!LittleFS.begin(true)) {
if (!LittleFS.begin(true))
{
Serial.println(F("An Error has occurred while mounting LittleFS")); Serial.println(F("An Error has occurred while mounting LittleFS"));
} }
@ -274,9 +269,7 @@ void setupHardware()
setupLeds(); setupLeds();
WiFi.setHostname(getMyHostname().c_str()); WiFi.setHostname(getMyHostname().c_str());
; if (!psramInit()) {
if (!psramInit())
{
Serial.println(F("PSRAM not available")); Serial.println(F("PSRAM not available"));
} }
@ -284,34 +277,28 @@ void setupHardware()
Wire.begin(I2C_SDA_PIN, I2C_SCK_PIN, 400000); Wire.begin(I2C_SDA_PIN, I2C_SCK_PIN, 400000);
if (!mcp1.begin_I2C(0x20)) if (!mcp1.begin_I2C(0x20)) {
{
Serial.println(F("Error MCP23017")); Serial.println(F("Error MCP23017"));
// while (1) // while (1)
// ; // ;
} } else {
else
{
pinMode(MCP_INT_PIN, INPUT_PULLUP); pinMode(MCP_INT_PIN, INPUT_PULLUP);
mcp1.setupInterrupts(false, false, LOW); mcp1.setupInterrupts(false, false, LOW);
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++) {
{
mcp1.pinMode(i, INPUT_PULLUP); mcp1.pinMode(i, INPUT_PULLUP);
mcp1.setupInterruptPin(i, LOW); mcp1.setupInterruptPin(i, LOW);
} }
#ifndef IS_BTCLOCK_S3 #ifndef IS_BTCLOCK_S3
for (int i = 8; i <= 14; i++) for (int i = 8; i <= 14; i++) {
{
mcp1.pinMode(i, OUTPUT); mcp1.pinMode(i, OUTPUT);
} }
#endif #endif
} }
#ifdef IS_BTCLOCK_S3 #ifdef IS_BTCLOCK_S3
if (!mcp2.begin_I2C(0x21)) if (!mcp2.begin_I2C(0x21)) {
{
Serial.println(F("Error MCP23017")); Serial.println(F("Error MCP23017"));
// while (1) // while (1)
@ -320,34 +307,32 @@ void setupHardware()
#endif #endif
} }
void improvGetAvailableWifiNetworks() void improvGetAvailableWifiNetworks() {
{
int networkNum = WiFi.scanNetworks(); int networkNum = WiFi.scanNetworks();
for (int id = 0; id < networkNum; ++id) for (int id = 0; id < networkNum; ++id) {
{
std::vector<uint8_t> data = improv::build_rpc_response( std::vector<uint8_t> data = improv::build_rpc_response(
improv::GET_WIFI_NETWORKS, {WiFi.SSID(id), String(WiFi.RSSI(id)), (WiFi.encryptionType(id) == WIFI_AUTH_OPEN ? "NO" : "YES")}, false); improv::GET_WIFI_NETWORKS,
{WiFi.SSID(id), String(WiFi.RSSI(id)),
(WiFi.encryptionType(id) == WIFI_AUTH_OPEN ? "NO" : "YES")},
false);
improv_send_response(data); improv_send_response(data);
} }
// final response // final response
std::vector<uint8_t> data = std::vector<uint8_t> data = improv::build_rpc_response(
improv::build_rpc_response(improv::GET_WIFI_NETWORKS, std::vector<std::string>{}, false); improv::GET_WIFI_NETWORKS, std::vector<std::string>{}, false);
improv_send_response(data); improv_send_response(data);
} }
bool improv_connectWifi(std::string ssid, std::string password) bool improv_connectWifi(std::string ssid, std::string password) {
{
uint8_t count = 0; uint8_t count = 0;
WiFi.begin(ssid.c_str(), password.c_str()); WiFi.begin(ssid.c_str(), password.c_str());
while (WiFi.status() != WL_CONNECTED) while (WiFi.status() != WL_CONNECTED) {
{
blinkDelay(500, 2); blinkDelay(500, 2);
if (count > MAX_ATTEMPTS_WIFI_CONNECTION) if (count > MAX_ATTEMPTS_WIFI_CONNECTION) {
{
WiFi.disconnect(); WiFi.disconnect();
return false; return false;
} }
@ -357,8 +342,7 @@ bool improv_connectWifi(std::string ssid, std::string password)
return true; return true;
} }
void onImprovErrorCallback(improv::Error err) void onImprovErrorCallback(improv::Error err) {
{
blinkDelayColor(100, 1, 255, 0, 0); blinkDelayColor(100, 1, 255, 0, 0);
// 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));
@ -372,39 +356,29 @@ void onImprovErrorCallback(improv::Error err)
// vTaskDelay(pdMS_TO_TICKS(100)); // vTaskDelay(pdMS_TO_TICKS(100));
} }
std::vector<std::string> getLocalUrl() std::vector<std::string> getLocalUrl() {
{ return {// URL where user can finish onboarding or use device
return {
// URL where user can finish onboarding or use device
// Recommended to use website hosted by device // Recommended to use website hosted by device
String("http://" + WiFi.localIP().toString()).c_str()}; String("http://" + WiFi.localIP().toString()).c_str()};
} }
bool onImprovCommandCallback(improv::ImprovCommand cmd) bool onImprovCommandCallback(improv::ImprovCommand cmd) {
{ switch (cmd.command) {
case improv::Command::GET_CURRENT_STATE: {
switch (cmd.command) if ((WiFi.status() == WL_CONNECTED)) {
{
case improv::Command::GET_CURRENT_STATE:
{
if ((WiFi.status() == WL_CONNECTED))
{
improv_set_state(improv::State::STATE_PROVISIONED); improv_set_state(improv::State::STATE_PROVISIONED);
std::vector<uint8_t> data = improv::build_rpc_response(improv::GET_CURRENT_STATE, getLocalUrl(), false); std::vector<uint8_t> data = improv::build_rpc_response(
improv::GET_CURRENT_STATE, getLocalUrl(), false);
improv_send_response(data); improv_send_response(data);
} } else {
else
{
improv_set_state(improv::State::STATE_AUTHORIZED); improv_set_state(improv::State::STATE_AUTHORIZED);
} }
break; break;
} }
case improv::Command::WIFI_SETTINGS: case improv::Command::WIFI_SETTINGS: {
{ if (cmd.ssid.length() == 0) {
if (cmd.ssid.length() == 0)
{
improv_set_error(improv::Error::ERROR_INVALID_RPC); improv_set_error(improv::Error::ERROR_INVALID_RPC);
break; break;
} }
@ -412,26 +386,23 @@ bool onImprovCommandCallback(improv::ImprovCommand cmd)
improv_set_state(improv::STATE_PROVISIONING); improv_set_state(improv::STATE_PROVISIONING);
queueLedEffect(LED_EFFECT_WIFI_CONNECTING); queueLedEffect(LED_EFFECT_WIFI_CONNECTING);
if (improv_connectWifi(cmd.ssid, cmd.password)) if (improv_connectWifi(cmd.ssid, cmd.password)) {
{
queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS); queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS);
// std::array<String, NUM_SCREENS> epdContent = {"S", "U", "C", "C", "E", "S", "S"}; // std::array<String, NUM_SCREENS> epdContent = {"S", "U", "C", "C", "E",
// setEpdContent(epdContent); // "S", "S"}; setEpdContent(epdContent);
preferences.putBool("wifiConfigured", true); preferences.putBool("wifiConfigured", true);
improv_set_state(improv::STATE_PROVISIONED); improv_set_state(improv::STATE_PROVISIONED);
std::vector<uint8_t> data = improv::build_rpc_response(improv::WIFI_SETTINGS, getLocalUrl(), false); std::vector<uint8_t> data = improv::build_rpc_response(
improv::WIFI_SETTINGS, getLocalUrl(), false);
improv_send_response(data); improv_send_response(data);
delay(2500); delay(2500);
ESP.restart(); ESP.restart();
setupWebserver(); setupWebserver();
} } else {
else
{
queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR); queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
improv_set_state(improv::STATE_STOPPED); improv_set_state(improv::STATE_STOPPED);
@ -441,10 +412,8 @@ bool onImprovCommandCallback(improv::ImprovCommand cmd)
break; break;
} }
case improv::Command::GET_DEVICE_INFO: case improv::Command::GET_DEVICE_INFO: {
{ std::vector<std::string> infos = {// Firmware name
std::vector<std::string> infos = {
// Firmware name
"BTClock", "BTClock",
// Firmware version // Firmware version
"1.0.0", "1.0.0",
@ -452,21 +421,20 @@ bool onImprovCommandCallback(improv::ImprovCommand cmd)
"ESP32S3", "ESP32S3",
// Device name // Device name
"BTClock"}; "BTClock"};
std::vector<uint8_t> data = improv::build_rpc_response(improv::GET_DEVICE_INFO, infos, false); std::vector<uint8_t> data =
improv::build_rpc_response(improv::GET_DEVICE_INFO, infos, false);
improv_send_response(data); improv_send_response(data);
break; break;
} }
case improv::Command::GET_WIFI_NETWORKS: case improv::Command::GET_WIFI_NETWORKS: {
{
improvGetAvailableWifiNetworks(); improvGetAvailableWifiNetworks();
// std::array<String, NUM_SCREENS> epdContent = {"W", "E", "B", "W", "I", "F", "I"}; // std::array<String, NUM_SCREENS> epdContent = {"W", "E", "B", "W", "I",
// setEpdContent(epdContent); // "F", "I"}; setEpdContent(epdContent);
break; break;
} }
default: default: {
{
improv_set_error(improv::ERROR_UNKNOWN_RPC); improv_set_error(improv::ERROR_UNKNOWN_RPC);
return false; return false;
} }
@ -475,9 +443,7 @@ bool onImprovCommandCallback(improv::ImprovCommand cmd)
return true; return true;
} }
void improv_set_state(improv::State state) void improv_set_state(improv::State state) {
{
std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'}; std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'};
data.resize(11); data.resize(11);
data[6] = improv::IMPROV_SERIAL_VERSION; data[6] = improv::IMPROV_SERIAL_VERSION;
@ -493,8 +459,7 @@ void improv_set_state(improv::State state)
Serial.write(data.data(), data.size()); Serial.write(data.data(), data.size());
} }
void improv_send_response(std::vector<uint8_t> &response) void improv_send_response(std::vector<uint8_t> &response) {
{
std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'}; std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'};
data.resize(9); data.resize(9);
data[6] = improv::IMPROV_SERIAL_VERSION; data[6] = improv::IMPROV_SERIAL_VERSION;
@ -510,8 +475,7 @@ void improv_send_response(std::vector<uint8_t> &response)
Serial.write(data.data(), data.size()); Serial.write(data.data(), data.size());
} }
void improv_set_error(improv::Error error) void improv_set_error(improv::Error error) {
{
std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'}; std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'};
data.resize(11); data.resize(11);
data[6] = improv::IMPROV_SERIAL_VERSION; data[6] = improv::IMPROV_SERIAL_VERSION;
@ -527,14 +491,12 @@ void improv_set_error(improv::Error error)
Serial.write(data.data(), data.size()); Serial.write(data.data(), data.size());
} }
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
{
static bool first_connect = true; static bool first_connect = true;
Serial.printf("[WiFi-event] event: %d\n", event); Serial.printf("[WiFi-event] event: %d\n", event);
switch (event) switch (event) {
{
case ARDUINO_EVENT_WIFI_READY: case ARDUINO_EVENT_WIFI_READY:
Serial.println("WiFi interface ready"); Serial.println("WiFi interface ready");
break; break;
@ -550,22 +512,21 @@ void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
case ARDUINO_EVENT_WIFI_STA_CONNECTED: case ARDUINO_EVENT_WIFI_STA_CONNECTED:
Serial.println("Connected to access point"); Serial.println("Connected to access point");
break; break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: {
{
if (!first_connect) { if (!first_connect) {
Serial.println("Disconnected from WiFi access point"); Serial.println("Disconnected from WiFi access point");
queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR); queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
uint8_t reason = info.wifi_sta_disconnected.reason; uint8_t reason = info.wifi_sta_disconnected.reason;
if (reason) if (reason)
Serial.printf("Disconnect reason: %s, ", WiFi.disconnectReasonName((wifi_err_reason_t)reason)); Serial.printf("Disconnect reason: %s, ",
WiFi.disconnectReasonName((wifi_err_reason_t)reason));
} }
break; break;
} }
case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE:
Serial.println("Authentication mode of access point has changed"); Serial.println("Authentication mode of access point has changed");
break; break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP: case ARDUINO_EVENT_WIFI_STA_GOT_IP: {
{
Serial.print("Obtained IP address: "); Serial.print("Obtained IP address: ");
Serial.println(WiFi.localIP()); Serial.println(WiFi.localIP());
if (!first_connect) if (!first_connect)
@ -613,7 +574,7 @@ String getMyHostname() {
esp_efuse_mac_get_default(mac); esp_efuse_mac_get_default(mac);
char hostname[15]; char hostname[15];
String hostnamePrefix = preferences.getString("hostnamePrefix", "btclock"); String hostnamePrefix = preferences.getString("hostnamePrefix", "btclock");
snprintf(hostname, sizeof(hostname), "%s-%02x%02x%02x", snprintf(hostname, sizeof(hostname), "%s-%02x%02x%02x", hostnamePrefix,
hostnamePrefix, mac[3], mac[4], mac[5]); mac[3], mac[4], mac[5]);
return hostname; return hostname;
} }

View File

@ -1,26 +1,23 @@
#pragma once; #pragma once;
#include <WiFiManager.h>
#include "base64.h"
#include <WiFiClientSecure.h>
#include <Preferences.h>
#include <Adafruit_MCP23X17.h> #include <Adafruit_MCP23X17.h>
#include <Arduino.h> #include <Arduino.h>
#include "shared.hpp" #include <Preferences.h>
#include "epd.hpp" #include <WiFiClientSecure.h>
#include "improv.hpp" #include <WiFiManager.h>
#include "esp_task_wdt.h" #include <base64.h>
#include <esp_task_wdt.h>
#include <map> #include <map>
#include "ota.hpp"
#include "lib/screen_handler.hpp"
#include "lib/webserver.hpp"
#include "lib/block_notify.hpp" #include "lib/block_notify.hpp"
#include "lib/price_notify.hpp"
#include "lib/button_handler.hpp" #include "lib/button_handler.hpp"
#include "lib/epd.hpp"
#include "lib/improv.hpp"
#include "lib/led_handler.hpp" #include "lib/led_handler.hpp"
#include "lib/ota.hpp"
#include "lib/price_notify.hpp"
#include "lib/screen_handler.hpp"
#include "lib/shared.hpp"
#include "lib/webserver.hpp"
#define NTP_SERVER "pool.ntp.org" #define NTP_SERVER "pool.ntp.org"
#define DEFAULT_MEMPOOL_INSTANCE "mempool.space" #define DEFAULT_MEMPOOL_INSTANCE "mempool.space"

View File

@ -16,21 +16,12 @@ Native_Pin EPD_CS[NUM_SCREENS] = {
#endif #endif
}; };
Native_Pin EPD_BUSY[NUM_SCREENS] = { Native_Pin EPD_BUSY[NUM_SCREENS] = {
Native_Pin(3), Native_Pin(3), Native_Pin(5), Native_Pin(7), Native_Pin(9),
Native_Pin(5), Native_Pin(37), Native_Pin(18), Native_Pin(16),
Native_Pin(7),
Native_Pin(9),
Native_Pin(37),
Native_Pin(18),
Native_Pin(16),
}; };
MCP23X17_Pin EPD_RESET_MPD[NUM_SCREENS] = { MCP23X17_Pin EPD_RESET_MPD[NUM_SCREENS] = {
MCP23X17_Pin(mcp1, 8), MCP23X17_Pin(mcp1, 8), MCP23X17_Pin(mcp1, 9), MCP23X17_Pin(mcp1, 10),
MCP23X17_Pin(mcp1, 9), MCP23X17_Pin(mcp1, 11), MCP23X17_Pin(mcp1, 12), MCP23X17_Pin(mcp1, 13),
MCP23X17_Pin(mcp1, 10),
MCP23X17_Pin(mcp1, 11),
MCP23X17_Pin(mcp1, 12),
MCP23X17_Pin(mcp1, 13),
MCP23X17_Pin(mcp1, 14), MCP23X17_Pin(mcp1, 14),
}; };
@ -39,36 +30,20 @@ Native_Pin EPD_DC = Native_Pin(14);
Native_Pin EPD_DC = Native_Pin(38); Native_Pin EPD_DC = Native_Pin(38);
MCP23X17_Pin EPD_BUSY[NUM_SCREENS] = { MCP23X17_Pin EPD_BUSY[NUM_SCREENS] = {
MCP23X17_Pin(mcp1, 8), MCP23X17_Pin(mcp1, 8), MCP23X17_Pin(mcp1, 9), MCP23X17_Pin(mcp1, 10),
MCP23X17_Pin(mcp1, 9), MCP23X17_Pin(mcp1, 11), MCP23X17_Pin(mcp1, 12), MCP23X17_Pin(mcp1, 13),
MCP23X17_Pin(mcp1, 10), MCP23X17_Pin(mcp1, 14), MCP23X17_Pin(mcp1, 4),
MCP23X17_Pin(mcp1, 11),
MCP23X17_Pin(mcp1, 12),
MCP23X17_Pin(mcp1, 13),
MCP23X17_Pin(mcp1, 14),
MCP23X17_Pin(mcp1, 4),
}; };
MCP23X17_Pin EPD_CS[NUM_SCREENS] = { MCP23X17_Pin EPD_CS[NUM_SCREENS] = {
MCP23X17_Pin(mcp2, 8), MCP23X17_Pin(mcp2, 8), MCP23X17_Pin(mcp2, 10), MCP23X17_Pin(mcp2, 12),
MCP23X17_Pin(mcp2, 10), MCP23X17_Pin(mcp2, 14), MCP23X17_Pin(mcp2, 0), MCP23X17_Pin(mcp2, 2),
MCP23X17_Pin(mcp2, 12), MCP23X17_Pin(mcp2, 4), MCP23X17_Pin(mcp2, 6)};
MCP23X17_Pin(mcp2, 14),
MCP23X17_Pin(mcp2, 0),
MCP23X17_Pin(mcp2, 2),
MCP23X17_Pin(mcp2, 4),
MCP23X17_Pin(mcp2, 6)
};
MCP23X17_Pin EPD_RESET_MPD[NUM_SCREENS] = { MCP23X17_Pin EPD_RESET_MPD[NUM_SCREENS] = {
MCP23X17_Pin(mcp2, 9), MCP23X17_Pin(mcp2, 9), MCP23X17_Pin(mcp2, 11), MCP23X17_Pin(mcp2, 13),
MCP23X17_Pin(mcp2, 11), MCP23X17_Pin(mcp2, 15), MCP23X17_Pin(mcp2, 1), MCP23X17_Pin(mcp2, 3),
MCP23X17_Pin(mcp2, 13), MCP23X17_Pin(mcp2, 5), MCP23X17_Pin(mcp2, 7),
MCP23X17_Pin(mcp2, 15),
MCP23X17_Pin(mcp2, 1),
MCP23X17_Pin(mcp2, 3),
MCP23X17_Pin(mcp2, 5),
MCP23X17_Pin(mcp2, 7),
}; };
#endif #endif
@ -108,37 +83,32 @@ std::mutex epdMutex[NUM_SCREENS];
uint8_t qrcode[800]; uint8_t qrcode[800];
void forceFullRefresh() void forceFullRefresh() {
{ for (uint i = 0; i < NUM_SCREENS; i++) {
for (uint i = 0; i < NUM_SCREENS; i++)
{
lastFullRefresh[i] = NULL; lastFullRefresh[i] = NULL;
} }
} }
void refreshFromMemory() void refreshFromMemory() {
{ for (uint i = 0; i < NUM_SCREENS; i++) {
for (uint i = 0; i < NUM_SCREENS; i++)
{
int *taskParam = new int; int *taskParam = new int;
*taskParam = i; *taskParam = i;
xTaskCreate([](void *pvParameters) xTaskCreate(
{ [](void *pvParameters) {
const int epdIndex = *(int *)pvParameters; const int epdIndex = *(int *)pvParameters;
delete (int *)pvParameters; delete (int *)pvParameters;
displays[epdIndex].refresh(false); displays[epdIndex].refresh(false);
vTaskDelete(NULL); }, vTaskDelete(NULL);
},
"PrepareUpd", 4096, taskParam, tskIDLE_PRIORITY, NULL); "PrepareUpd", 4096, taskParam, tskIDLE_PRIORITY, NULL);
} }
} }
void setupDisplays() void setupDisplays() {
{
std::lock_guard<std::mutex> lockMcp(mcpMutex); std::lock_guard<std::mutex> lockMcp(mcpMutex);
for (uint i = 0; i < NUM_SCREENS; i++) for (uint i = 0; i < NUM_SCREENS; i++) {
{
displays[i].init(0, true, 30); displays[i].init(0, true, 30);
} }
@ -146,30 +116,23 @@ void setupDisplays()
xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", 4096, NULL, 11, NULL); xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", 4096, NULL, 11, NULL);
for (uint i = 0; i < NUM_SCREENS; i++) for (uint i = 0; i < NUM_SCREENS; i++) {
{
// epdUpdateSemaphore[i] = xSemaphoreCreateBinary(); // epdUpdateSemaphore[i] = xSemaphoreCreateBinary();
// xSemaphoreGive(epdUpdateSemaphore[i]); // xSemaphoreGive(epdUpdateSemaphore[i]);
int *taskParam = new int; int *taskParam = new int;
*taskParam = i; *taskParam = i;
xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), 2048, taskParam, 11, &tasks[i]); // create task xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), 2048, taskParam,
11, &tasks[i]); // create task
} }
epdContent = {"B", epdContent = {"B", "T", "C", "L", "O", "C", "K"};
"T",
"C",
"L",
"O",
"C",
"K"};
setEpdContent(epdContent); setEpdContent(epdContent);
} }
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent) void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent) {
{
setEpdContent(newEpdContent, false); setEpdContent(newEpdContent, false);
} }
@ -183,16 +146,14 @@ void setEpdContent(std::array<std::string, NUM_SCREENS> newEpdContent) {
return setEpdContent(conv); return setEpdContent(conv);
} }
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent, bool forceUpdate) void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent,
{ bool forceUpdate) {
std::lock_guard<std::mutex> lock(epdUpdateMutex); std::lock_guard<std::mutex> lock(epdUpdateMutex);
waitUntilNoneBusy(); waitUntilNoneBusy();
for (uint i = 0; i < NUM_SCREENS; i++) for (uint i = 0; i < NUM_SCREENS; i++) {
{ if (newEpdContent[i].compareTo(currentEpdContent[i]) != 0 || forceUpdate) {
if (newEpdContent[i].compareTo(currentEpdContent[i]) != 0 || forceUpdate)
{
epdContent[i] = newEpdContent[i]; epdContent[i] = newEpdContent[i];
UpdateDisplayTaskItem dispUpdate = {i}; UpdateDisplayTaskItem dispUpdate = {i};
xQueueSend(updateQueue, &dispUpdate, portMAX_DELAY); xQueueSend(updateQueue, &dispUpdate, portMAX_DELAY);
@ -200,45 +161,37 @@ void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent, bool forceUpda
} }
} }
void prepareDisplayUpdateTask(void *pvParameters) void prepareDisplayUpdateTask(void *pvParameters) {
{
UpdateDisplayTaskItem receivedItem; UpdateDisplayTaskItem receivedItem;
while (1) while (1) {
{
// Wait for a work item to be available in the queue // Wait for a work item to be available in the queue
if (xQueueReceive(updateQueue, &receivedItem, portMAX_DELAY)) if (xQueueReceive(updateQueue, &receivedItem, portMAX_DELAY)) {
{
uint epdIndex = receivedItem.dispNum; uint epdIndex = receivedItem.dispNum;
std::lock_guard<std::mutex> lock(epdMutex[epdIndex]); std::lock_guard<std::mutex> lock(epdMutex[epdIndex]);
// displays[epdIndex].init(0, false); // Little longer reset duration because of MCP // displays[epdIndex].init(0, false); // Little longer reset duration
// because of MCP
bool updatePartial = true; bool updatePartial = true;
if (strstr(epdContent[epdIndex].c_str(), "/") != NULL) if (strstr(epdContent[epdIndex].c_str(), "/") != NULL) {
{ String top = epdContent[epdIndex].substring(
String top = epdContent[epdIndex].substring(0, epdContent[epdIndex].indexOf("/")); 0, epdContent[epdIndex].indexOf("/"));
String bottom = epdContent[epdIndex].substring(epdContent[epdIndex].indexOf("/") + 1); String bottom = epdContent[epdIndex].substring(
epdContent[epdIndex].indexOf("/") + 1);
splitText(epdIndex, top, bottom, updatePartial); splitText(epdIndex, top, bottom, updatePartial);
} } else if (epdContent[epdIndex].startsWith(F("qr"))) {
else if (epdContent[epdIndex].startsWith(F("qr")))
{
renderQr(epdIndex, epdContent[epdIndex], updatePartial); renderQr(epdIndex, epdContent[epdIndex], updatePartial);
} } else if (epdContent[epdIndex].length() > 5) {
else if (epdContent[epdIndex].length() > 5)
{
renderText(epdIndex, epdContent[epdIndex], updatePartial); renderText(epdIndex, epdContent[epdIndex], updatePartial);
} } else {
else
{
if (epdContent[epdIndex].length() > 1) if (epdContent[epdIndex].length() > 1) {
{ showChars(epdIndex, epdContent[epdIndex], updatePartial,
showChars(epdIndex, epdContent[epdIndex], updatePartial, &FONT_MEDIUM); &FONT_MEDIUM);
} } else {
else showDigit(epdIndex, epdContent[epdIndex].c_str()[0], updatePartial,
{ &FONT_BIG);
showDigit(epdIndex, epdContent[epdIndex].c_str()[0], updatePartial, &FONT_BIG);
} }
} }
@ -247,13 +200,11 @@ void prepareDisplayUpdateTask(void *pvParameters)
} }
} }
extern "C" void updateDisplay(void *pvParameters) noexcept extern "C" void updateDisplay(void *pvParameters) noexcept {
{
const int epdIndex = *(int *)pvParameters; const int epdIndex = *(int *)pvParameters;
delete (int *)pvParameters; delete (int *)pvParameters;
for (;;) for (;;) {
{
// Wait for the task notification // Wait for the task notification
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
@ -265,8 +216,7 @@ extern "C" void updateDisplay(void *pvParameters) noexcept
displays[epdIndex].init(0, false, 40); displays[epdIndex].init(0, false, 40);
} }
uint count = 0; uint count = 0;
while (EPD_BUSY[epdIndex].digitalRead() == HIGH || count < 10) while (EPD_BUSY[epdIndex].digitalRead() == HIGH || count < 10) {
{
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(100));
count++; count++;
} }
@ -274,16 +224,17 @@ extern "C" void updateDisplay(void *pvParameters) noexcept
bool updatePartial = true; bool updatePartial = true;
// Full Refresh every x minutes // Full Refresh every x minutes
if (!lastFullRefresh[epdIndex] || (millis() - lastFullRefresh[epdIndex]) > (preferences.getUInt("fullRefreshMin", DEFAULT_MINUTES_FULL_REFRESH) * 60 * 1000)) if (!lastFullRefresh[epdIndex] ||
{ (millis() - lastFullRefresh[epdIndex]) >
(preferences.getUInt("fullRefreshMin",
DEFAULT_MINUTES_FULL_REFRESH) *
60 * 1000)) {
updatePartial = false; updatePartial = false;
} }
char tries = 0; char tries = 0;
while (tries < 3) while (tries < 3) {
{ if (displays[epdIndex].displayWithReturn(updatePartial)) {
if (displays[epdIndex].displayWithReturn(updatePartial))
{
displays[epdIndex].powerOff(); displays[epdIndex].powerOff();
currentEpdContent[epdIndex] = epdContent[epdIndex]; currentEpdContent[epdIndex] = epdContent[epdIndex];
if (!updatePartial) if (!updatePartial)
@ -301,8 +252,8 @@ extern "C" void updateDisplay(void *pvParameters) noexcept
} }
} }
void splitText(const uint dispNum, const String &top, const String &bottom, bool partial) void splitText(const uint dispNum, const String &top, const String &bottom,
{ bool partial) {
displays[dispNum].setRotation(2); displays[dispNum].setRotation(2);
displays[dispNum].setFont(&FONT_SMALL); displays[dispNum].setFont(&FONT_SMALL);
displays[dispNum].setTextColor(getFgColor()); displays[dispNum].setTextColor(getFgColor());
@ -312,14 +263,16 @@ void splitText(const uint dispNum, const String &top, const String &bottom, bool
uint16_t ttbw, ttbh; uint16_t ttbw, ttbh;
displays[dispNum].getTextBounds(top, 0, 0, &ttbx, &ttby, &ttbw, &ttbh); displays[dispNum].getTextBounds(top, 0, 0, &ttbx, &ttby, &ttbw, &ttbh);
uint16_t tx = ((displays[dispNum].width() - ttbw) / 2) - ttbx; uint16_t tx = ((displays[dispNum].width() - ttbw) / 2) - ttbx;
uint16_t ty = ((displays[dispNum].height() - ttbh) / 2) - ttby - ttbh / 2 - 12; uint16_t ty =
((displays[dispNum].height() - ttbh) / 2) - ttby - ttbh / 2 - 12;
// Bottom text // Bottom text
int16_t tbbx, tbby; int16_t tbbx, tbby;
uint16_t tbbw, tbbh; uint16_t tbbw, tbbh;
displays[dispNum].getTextBounds(bottom, 0, 0, &tbbx, &tbby, &tbbw, &tbbh); displays[dispNum].getTextBounds(bottom, 0, 0, &tbbx, &tbby, &tbbw, &tbbh);
uint16_t bx = ((displays[dispNum].width() - tbbw) / 2) - tbbx; uint16_t bx = ((displays[dispNum].width() - tbbw) / 2) - tbbx;
uint16_t by = ((displays[dispNum].height() - tbbh) / 2) - tbby + tbbh / 2 + 12; uint16_t by =
((displays[dispNum].height() - tbbh) / 2) - tbby + tbbh / 2 + 12;
// Make separator as wide as the shortest text. // Make separator as wide as the shortest text.
uint16_t lineWidth, lineX; uint16_t lineWidth, lineX;
@ -332,13 +285,14 @@ void splitText(const uint dispNum, const String &top, const String &bottom, bool
displays[dispNum].fillScreen(getBgColor()); displays[dispNum].fillScreen(getBgColor());
displays[dispNum].setCursor(tx, ty); displays[dispNum].setCursor(tx, ty);
displays[dispNum].print(top); displays[dispNum].print(top);
displays[dispNum].fillRoundRect(lineX, displays[dispNum].height() / 2 - 3, lineWidth, 6, 3, getFgColor()); displays[dispNum].fillRoundRect(lineX, displays[dispNum].height() / 2 - 3,
lineWidth, 6, 3, getFgColor());
displays[dispNum].setCursor(bx, by); displays[dispNum].setCursor(bx, by);
displays[dispNum].print(bottom); displays[dispNum].print(bottom);
} }
void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font) void showDigit(const uint dispNum, char chr, bool partial,
{ const GFXfont *font) {
String str(chr); String str(chr);
displays[dispNum].setRotation(2); displays[dispNum].setRotation(2);
displays[dispNum].setFont(font); displays[dispNum].setFont(font);
@ -354,8 +308,8 @@ void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font)
displays[dispNum].print(str); displays[dispNum].print(str);
} }
void showChars(const uint dispNum, const String &chars, bool partial, const GFXfont *font) void showChars(const uint dispNum, const String &chars, bool partial,
{ const GFXfont *font) {
displays[dispNum].setRotation(2); displays[dispNum].setRotation(2);
displays[dispNum].setFont(font); displays[dispNum].setFont(font);
displays[dispNum].setTextColor(getFgColor()); displays[dispNum].setTextColor(getFgColor());
@ -370,34 +324,21 @@ void showChars(const uint dispNum, const String &chars, bool partial, const GFXf
displays[dispNum].print(chars); displays[dispNum].print(chars);
} }
int getBgColor() int getBgColor() { return bgColor; }
{
return bgColor;
}
int getFgColor() int getFgColor() { return fgColor; }
{
return fgColor;
}
void setBgColor(int color) void setBgColor(int color) { bgColor = color; }
{
bgColor = color;
}
void setFgColor(int color) void setFgColor(int color) { fgColor = color; }
{
fgColor = color;
}
std::array<String, NUM_SCREENS> getCurrentEpdContent() std::array<String, NUM_SCREENS> getCurrentEpdContent() {
{
return currentEpdContent; return currentEpdContent;
} }
void renderText(const uint dispNum, const String &text, bool partial) void renderText(const uint dispNum, const String &text, bool partial) {
{
displays[dispNum].setRotation(2); displays[dispNum].setRotation(2);
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), displays[dispNum].height()); displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(),
displays[dispNum].height());
displays[dispNum].fillScreen(GxEPD_WHITE); displays[dispNum].fillScreen(GxEPD_WHITE);
displays[dispNum].setTextColor(GxEPD_BLACK); displays[dispNum].setTextColor(GxEPD_BLACK);
displays[dispNum].setCursor(0, 50); displays[dispNum].setCursor(0, 50);
@ -407,67 +348,61 @@ void renderText(const uint dispNum, const String &text, bool partial)
std::string line; std::string line;
while (std::getline(ss, line, '\n')) while (std::getline(ss, line, '\n')) {
{ if (line.rfind("*", 0) == 0) {
if (line.rfind("*", 0) == 0)
{
line.erase(std::remove(line.begin(), line.end(), '*'), line.end()); line.erase(std::remove(line.begin(), line.end(), '*'), line.end());
displays[dispNum].setFont(&FreeSansBold9pt7b); displays[dispNum].setFont(&FreeSansBold9pt7b);
displays[dispNum].println(line.c_str()); displays[dispNum].println(line.c_str());
} } else {
else
{
displays[dispNum].setFont(&FreeSans9pt7b); displays[dispNum].setFont(&FreeSans9pt7b);
displays[dispNum].println(line.c_str()); displays[dispNum].println(line.c_str());
} }
} }
} }
void renderQr(const uint dispNum, const String &text, bool partial) void renderQr(const uint dispNum, const String &text, bool partial) {
{
#ifdef USE_QR #ifdef USE_QR
uint8_t tempBuffer[800]; uint8_t tempBuffer[800];
bool ok = qrcodegen_encodeText(text.substring(2).c_str(), tempBuffer, qrcode, qrcodegen_Ecc_LOW, bool ok = qrcodegen_encodeText(
text.substring(2).c_str(), tempBuffer, qrcode, qrcodegen_Ecc_LOW,
qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true);
const int size = qrcodegen_getSize(qrcode); const int size = qrcodegen_getSize(qrcode);
const int padding = floor(float(displays[dispNum].width() - (size * 4)) / 2); const int padding = floor(float(displays[dispNum].width() - (size * 4)) / 2);
const int paddingY = floor(float(displays[dispNum].height() - (size * 4)) / 2); const int paddingY =
floor(float(displays[dispNum].height() - (size * 4)) / 2);
displays[dispNum].setRotation(2); displays[dispNum].setRotation(2);
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), displays[dispNum].height()); displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(),
displays[dispNum].height());
displays[dispNum].fillScreen(GxEPD_WHITE); displays[dispNum].fillScreen(GxEPD_WHITE);
const int border = 0; const int border = 0;
for (int y = -border; y < size * 4 + border; y++) for (int y = -border; y < size * 4 + border; y++) {
{ for (int x = -border; x < size * 4 + border; x++) {
for (int x = -border; x < size * 4 + border; x++) displays[dispNum].drawPixel(
{ padding + x, paddingY + y,
displays[dispNum].drawPixel(padding + x, paddingY + y, qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4)) ? GxEPD_BLACK : GxEPD_WHITE); qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4))
? GxEPD_BLACK
: GxEPD_WHITE);
} }
} }
#endif #endif
} }
void waitUntilNoneBusy() void waitUntilNoneBusy() {
{ for (int i = 0; i < NUM_SCREENS; i++) {
for (int i = 0; i < NUM_SCREENS; i++)
{
uint count = 0; uint count = 0;
while (EPD_BUSY[i].digitalRead()) while (EPD_BUSY[i].digitalRead()) {
{
count++; count++;
vTaskDelay(10); vTaskDelay(10);
if (count == 200) if (count == 200) {
{
// displays[i].init(0, false); // displays[i].init(0, false);
vTaskDelay(100); vTaskDelay(100);
} } else if (count > 205) {
else if (count > 205)
{
Serial.printf("Busy timeout %d", i); Serial.printf("Busy timeout %d", i);
break; break;
} }

View File

@ -1,23 +1,22 @@
#pragma once #pragma once
#include <GxEPD2_BW.h>
#include <native_pin.hpp>
#include <mcp23x17_pin.hpp>
#include "shared.hpp"
#include "config.hpp"
#include "fonts/fonts.hpp" #include "fonts/fonts.hpp"
#include <Fonts/FreeSansBold9pt7b.h> #include "lib/config.hpp"
#include "lib/shared.hpp"
#include <Fonts/FreeSans9pt7b.h> #include <Fonts/FreeSans9pt7b.h>
#include <regex> #include <Fonts/FreeSansBold9pt7b.h>
#include <GxEPD2_BW.h>
#include <mcp23x17_pin.hpp>
#include <mutex> #include <mutex>
#include <native_pin.hpp>
#include <regex>
#ifdef USE_QR #ifdef USE_QR
#include "qrcodegen.h" #include "qrcodegen.h"
#endif #endif
// extern TaskHandle_t epdTaskHandle; // extern TaskHandle_t epdTaskHandle;
typedef struct typedef struct {
{
char dispNum; char dispNum;
} UpdateDisplayTaskItem; } UpdateDisplayTaskItem;
@ -25,10 +24,12 @@ void forceFullRefresh();
void refreshFromMemory(); void refreshFromMemory();
void setupDisplays(); void setupDisplays();
void splitText(const uint dispNum, const String& top, const String& bottom, bool partial); void splitText(const uint dispNum, const String &top, const String &bottom,
bool partial);
void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font); void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font);
void showChars(const uint dispNum, const String& chars, bool partial, const GFXfont *font); void showChars(const uint dispNum, const String &chars, bool partial,
const GFXfont *font);
extern "C" void updateDisplay(void *pvParameters) noexcept; extern "C" void updateDisplay(void *pvParameters) noexcept;
void updateDisplayAlt(int epdIndex); void updateDisplayAlt(int epdIndex);
@ -42,7 +43,8 @@ void setFgColor(int color);
void renderText(const uint dispNum, const String &text, bool partial); void renderText(const uint dispNum, const String &text, bool partial);
void renderQr(const uint dispNum, const String &text, bool partial); void renderQr(const uint dispNum, const String &text, bool partial);
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent, bool forceUpdate); void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent,
bool forceUpdate);
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent); void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent);
void setEpdContent(std::array<std::string, NUM_SCREENS> newEpdContent); void setEpdContent(std::array<std::string, NUM_SCREENS> newEpdContent);

View File

@ -2,11 +2,13 @@
namespace improv { namespace improv {
ImprovCommand parse_improv_data(const std::vector<uint8_t> &data, bool check_checksum) { ImprovCommand parse_improv_data(const std::vector<uint8_t> &data,
bool check_checksum) {
return parse_improv_data(data.data(), data.size(), check_checksum); return parse_improv_data(data.data(), data.size(), check_checksum);
} }
ImprovCommand parse_improv_data(const uint8_t *data, size_t length, bool check_checksum) { ImprovCommand parse_improv_data(const uint8_t *data, size_t length,
bool check_checksum) {
ImprovCommand improv_command; ImprovCommand improv_command;
Command command = (Command)data[0]; Command command = (Command)data[0];
uint8_t data_length = data[1]; uint8_t data_length = data[1];
@ -48,8 +50,10 @@ ImprovCommand parse_improv_data(const uint8_t *data, size_t length, bool check_c
return improv_command; return improv_command;
} }
bool parse_improv_serial_byte(size_t position, uint8_t byte, const uint8_t *buffer, bool parse_improv_serial_byte(size_t position, uint8_t byte,
std::function<bool(ImprovCommand)> &&callback, std::function<void(Error)> &&on_error) { const uint8_t *buffer,
std::function<bool(ImprovCommand)> &&callback,
std::function<void(Error)> &&on_error) {
if (position == 0) if (position == 0)
return byte == 'I'; return byte == 'I';
if (position == 1) if (position == 1)
@ -94,7 +98,9 @@ bool parse_improv_serial_byte(size_t position, uint8_t byte, const uint8_t *buff
return false; return false;
} }
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<std::string> &datum, bool add_checksum) { std::vector<uint8_t> build_rpc_response(Command command,
const std::vector<std::string> &datum,
bool add_checksum) {
std::vector<uint8_t> out; std::vector<uint8_t> out;
uint32_t length = 0; uint32_t length = 0;
out.push_back(command); out.push_back(command);
@ -118,7 +124,9 @@ std::vector<uint8_t> build_rpc_response(Command command, const std::vector<std::
} }
#ifdef ARDUINO #ifdef ARDUINO
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<String> &datum, bool add_checksum) { std::vector<uint8_t> build_rpc_response(Command command,
const std::vector<String> &datum,
bool add_checksum) {
std::vector<uint8_t> out; std::vector<uint8_t> out;
uint32_t length = 0; uint32_t length = 0;
out.push_back(command); out.push_back(command);

View File

@ -14,9 +14,12 @@ namespace improv {
static const char *const SERVICE_UUID = "00467768-6228-2272-4663-277478268000"; static const char *const SERVICE_UUID = "00467768-6228-2272-4663-277478268000";
static const char *const STATUS_UUID = "00467768-6228-2272-4663-277478268001"; static const char *const STATUS_UUID = "00467768-6228-2272-4663-277478268001";
static const char *const ERROR_UUID = "00467768-6228-2272-4663-277478268002"; static const char *const ERROR_UUID = "00467768-6228-2272-4663-277478268002";
static const char *const RPC_COMMAND_UUID = "00467768-6228-2272-4663-277478268003"; static const char *const RPC_COMMAND_UUID =
static const char *const RPC_RESULT_UUID = "00467768-6228-2272-4663-277478268004"; "00467768-6228-2272-4663-277478268003";
static const char *const CAPABILITIES_UUID = "00467768-6228-2272-4663-277478268005"; static const char *const RPC_RESULT_UUID =
"00467768-6228-2272-4663-277478268004";
static const char *const CAPABILITIES_UUID =
"00467768-6228-2272-4663-277478268005";
enum Error : uint8_t { enum Error : uint8_t {
ERROR_NONE = 0x00, ERROR_NONE = 0x00,
@ -61,16 +64,23 @@ struct ImprovCommand {
std::string password; std::string password;
}; };
ImprovCommand parse_improv_data(const std::vector<uint8_t> &data, bool check_checksum = true); ImprovCommand parse_improv_data(const std::vector<uint8_t> &data,
ImprovCommand parse_improv_data(const uint8_t *data, size_t length, bool check_checksum = true); bool check_checksum = true);
ImprovCommand parse_improv_data(const uint8_t *data, size_t length,
bool check_checksum = true);
bool parse_improv_serial_byte(size_t position, uint8_t byte, const uint8_t *buffer, bool parse_improv_serial_byte(size_t position, uint8_t byte,
std::function<bool(ImprovCommand)> &&callback, std::function<void(Error)> &&on_error); const uint8_t *buffer,
std::function<bool(ImprovCommand)> &&callback,
std::function<void(Error)> &&on_error);
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<std::string> &datum, std::vector<uint8_t> build_rpc_response(Command command,
const std::vector<std::string> &datum,
bool add_checksum = true); bool add_checksum = true);
#ifdef ARDUINO #ifdef ARDUINO
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<String> &datum, bool add_checksum = true); std::vector<uint8_t> build_rpc_response(Command command,
const std::vector<String> &datum,
bool add_checksum = true);
#endif // ARDUINO #endif // ARDUINO
} // namespace improv } // namespace improv

View File

@ -5,30 +5,26 @@ QueueHandle_t ledTaskQueue = NULL;
Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800); Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
uint ledTaskParams; uint ledTaskParams;
void ledTask(void *parameter) void ledTask(void *parameter) {
{ while (1) {
while (1) if (ledTaskQueue != NULL) {
{ if (xQueueReceive(ledTaskQueue, &ledTaskParams, portMAX_DELAY) ==
if (ledTaskQueue != NULL) pdPASS) {
{
if (xQueueReceive(ledTaskQueue, &ledTaskParams, portMAX_DELAY) == pdPASS)
{
uint32_t oldLights[NEOPIXEL_COUNT]; uint32_t oldLights[NEOPIXEL_COUNT];
// get current state // get current state
for (int i = 0; i < NEOPIXEL_COUNT; i++) for (int i = 0; i < NEOPIXEL_COUNT; i++) {
{
oldLights[i] = pixels.getPixelColor(i); oldLights[i] = pixels.getPixelColor(i);
} }
switch (ledTaskParams) switch (ledTaskParams) {
{
case LED_POWER_TEST: case LED_POWER_TEST:
ledRainbow(20); ledRainbow(20);
pixels.clear(); pixels.clear();
break; break;
case LED_EFFECT_WIFI_CONNECT_ERROR: case LED_EFFECT_WIFI_CONNECT_ERROR:
blinkDelayTwoColor(100, 3, pixels.Color(8, 161, 236), pixels.Color(255, 0, 0)); blinkDelayTwoColor(100, 3, pixels.Color(8, 161, 236),
pixels.Color(255, 0, 0));
break; break;
case LED_FLASH_ERROR: case LED_FLASH_ERROR:
blinkDelayColor(250, 3, 255, 0, 0); blinkDelayColor(250, 3, 255, 0, 0);
@ -53,25 +49,22 @@ void ledTask(void *parameter)
case LED_FLASH_UPDATE: case LED_FLASH_UPDATE:
break; break;
case LED_FLASH_BLOCK_NOTIFY: case LED_FLASH_BLOCK_NOTIFY:
blinkDelayTwoColor(250, 3, pixels.Color(224, 67, 0), pixels.Color(8, 2, 0)); blinkDelayTwoColor(250, 3, pixels.Color(224, 67, 0),
pixels.Color(8, 2, 0));
break; break;
case LED_EFFECT_WIFI_WAIT_FOR_CONFIG: case LED_EFFECT_WIFI_WAIT_FOR_CONFIG:
blinkDelayTwoColor(100, 1, pixels.Color(8, 161, 236), pixels.Color(156, 225, 240)); blinkDelayTwoColor(100, 1, pixels.Color(8, 161, 236),
pixels.Color(156, 225, 240));
break; break;
case LED_EFFECT_WIFI_ERASE_SETTINGS: case LED_EFFECT_WIFI_ERASE_SETTINGS:
blinkDelay(100, 3); blinkDelay(100, 3);
break; break;
case LED_EFFECT_WIFI_CONNECTING: case LED_EFFECT_WIFI_CONNECTING:
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--) if (j == i) {
{
if (j == i)
{
pixels.setPixelColor(i, pixels.Color(16, 197, 236)); pixels.setPixelColor(i, pixels.Color(16, 197, 236));
} } else {
else
{
pixels.setPixelColor(j, pixels.Color(0, 0, 0)); pixels.setPixelColor(j, pixels.Color(0, 0, 0));
} }
} }
@ -80,10 +73,8 @@ void ledTask(void *parameter)
} }
break; break;
case LED_EFFECT_PAUSE_TIMER: case LED_EFFECT_PAUSE_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--)
{
uint32_t c = pixels.Color(0, 0, 0); uint32_t c = pixels.Color(0, 0, 0);
if (i == j) if (i == j)
c = pixels.Color(0, 255, 0); c = pixels.Color(0, 255, 0);
@ -109,11 +100,9 @@ void ledTask(void *parameter)
delay(900); delay(900);
for (int i = NEOPIXEL_COUNT; i--; i > 0) for (int i = NEOPIXEL_COUNT; i--; i > 0) {
{
for (int j = NEOPIXEL_COUNT; j--; j > 0) for (int j = NEOPIXEL_COUNT; j--; j > 0) {
{
uint32_t c = pixels.Color(0, 0, 0); uint32_t c = pixels.Color(0, 0, 0);
if (i == j) if (i == j)
c = pixels.Color(0, 255, 0); c = pixels.Color(0, 255, 0);
@ -133,8 +122,7 @@ void ledTask(void *parameter)
// revert to previous state unless power test // revert to previous state unless power test
for (int i = 0; i < NEOPIXEL_COUNT; i++) for (int i = 0; i < NEOPIXEL_COUNT; i++) {
{
pixels.setPixelColor(i, oldLights[i]); pixels.setPixelColor(i, oldLights[i]);
} }
@ -144,17 +132,14 @@ void ledTask(void *parameter)
} }
} }
void setupLeds() void setupLeds() {
{
pixels.begin(); pixels.begin();
pixels.setBrightness(preferences.getUInt("ledBrightness", 128)); pixels.setBrightness(preferences.getUInt("ledBrightness", 128));
pixels.clear(); pixels.clear();
pixels.show(); pixels.show();
setupLedTask(); setupLedTask();
if (preferences.getBool("ledTestOnPower", true)) if (preferences.getBool("ledTestOnPower", true)) {
{ while (!ledTaskQueue) {
while (!ledTaskQueue)
{
delay(1); delay(1);
// wait until queue is available // wait until queue is available
} }
@ -162,17 +147,14 @@ void setupLeds()
} }
} }
void setupLedTask() void setupLedTask() {
{
ledTaskQueue = xQueueCreate(5, sizeof(uint)); ledTaskQueue = xQueueCreate(5, sizeof(uint));
xTaskCreate(ledTask, "LedTask", 2048, NULL, tskIDLE_PRIORITY, &ledTaskHandle); xTaskCreate(ledTask, "LedTask", 2048, NULL, tskIDLE_PRIORITY, &ledTaskHandle);
} }
void blinkDelay(int d, int times) void blinkDelay(int d, int times) {
{ for (int j = 0; j < times; j++) {
for (int j = 0; j < times; j++)
{
pixels.setPixelColor(0, pixels.Color(255, 0, 0)); pixels.setPixelColor(0, pixels.Color(255, 0, 0));
pixels.setPixelColor(1, pixels.Color(0, 255, 0)); pixels.setPixelColor(1, pixels.Color(0, 255, 0));
@ -192,12 +174,9 @@ void blinkDelay(int d, int times)
pixels.show(); pixels.show();
} }
void blinkDelayColor(int d, int times, uint r, uint g, uint b) void blinkDelayColor(int d, int times, uint r, uint g, uint b) {
{ for (int j = 0; j < times; j++) {
for (int j = 0; j < times; j++) for (int i = 0; i < NEOPIXEL_COUNT; i++) {
{
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
pixels.setPixelColor(i, pixels.Color(r, g, b)); pixels.setPixelColor(i, pixels.Color(r, g, b));
} }
@ -212,19 +191,15 @@ void blinkDelayColor(int d, int times, uint r, uint g, uint b)
pixels.show(); pixels.show();
} }
void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2) void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2) {
{ for (int j = 0; j < times; j++) {
for (int j = 0; j < times; j++) for (int i = 0; i < NEOPIXEL_COUNT; i++) {
{
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
pixels.setPixelColor(i, c1); pixels.setPixelColor(i, c1);
} }
pixels.show(); pixels.show();
vTaskDelay(pdMS_TO_TICKS(d)); vTaskDelay(pdMS_TO_TICKS(d));
for (int i = 0; i < NEOPIXEL_COUNT; i++) for (int i = 0; i < NEOPIXEL_COUNT; i++) {
{
pixels.setPixelColor(i, c2); pixels.setPixelColor(i, c2);
} }
pixels.show(); pixels.show();
@ -234,43 +209,33 @@ void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2)
pixels.show(); pixels.show();
} }
void clearLeds() void clearLeds() {
{
preferences.putBool("ledStatus", false); preferences.putBool("ledStatus", false);
pixels.clear(); pixels.clear();
pixels.show(); pixels.show();
} }
void setLights(int r, int g, int b) void setLights(int r, int g, int b) { setLights(pixels.Color(r, g, b)); }
{
setLights(pixels.Color(r, g, b));
}
void setLights(uint32_t color) void setLights(uint32_t color) {
{
bool ledStatus = true; bool ledStatus = true;
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
pixels.setPixelColor(i, color); pixels.setPixelColor(i, color);
} }
pixels.show(); pixels.show();
if (color == pixels.Color(0, 0, 0)) if (color == pixels.Color(0, 0, 0)) {
{
ledStatus = false; ledStatus = false;
} else { } else {
saveLedState(); saveLedState();
} }
preferences.putBool("ledStatus", ledStatus); preferences.putBool("ledStatus", ledStatus);
} }
void saveLedState() { void saveLedState() {
for (int i = 0; i < pixels.numPixels(); i++) for (int i = 0; i < pixels.numPixels(); i++) {
{
int pixelColor = pixels.getPixelColor(i); int pixelColor = pixels.getPixelColor(i);
char key[12]; char key[12];
snprintf(key, 12, "%s%d", "ledColor_", i); snprintf(key, 12, "%s%d", "ledColor_", i);
@ -281,8 +246,7 @@ void saveLedState() {
} }
void restoreLedState() { void restoreLedState() {
for (int i = 0; i < pixels.numPixels(); i++) for (int i = 0; i < pixels.numPixels(); i++) {
{
char key[12]; char key[12];
snprintf(key, 12, "%s%d", "ledColor_", i); snprintf(key, 12, "%s%d", "ledColor_", i);
uint pixelColor = preferences.getUInt(key, pixels.Color(0, 0, 0)); uint pixelColor = preferences.getUInt(key, pixels.Color(0, 0, 0));
@ -292,15 +256,10 @@ void restoreLedState() {
pixels.show(); pixels.show();
} }
QueueHandle_t getLedTaskQueue() QueueHandle_t getLedTaskQueue() { return ledTaskQueue; }
{
return ledTaskQueue;
}
bool queueLedEffect(uint effect) bool queueLedEffect(uint effect) {
{ if (ledTaskQueue == NULL) {
if (ledTaskQueue == NULL)
{
return false; return false;
} }
@ -308,14 +267,13 @@ bool queueLedEffect(uint effect)
xQueueSend(ledTaskQueue, &flashType, portMAX_DELAY); xQueueSend(ledTaskQueue, &flashType, portMAX_DELAY);
} }
void ledRainbow(int wait) void ledRainbow(int wait) {
{
// Hue of first pixel runs 5 complete loops through the color wheel. // Hue of first pixel runs 5 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so // Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 5*65536. Adding 256 to firstPixelHue each time // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
// means we'll make 5*65536/256 = 1280 passes through this loop: // means we'll make 5*65536/256 = 1280 passes through this loop:
for (long firstPixelHue = 0; firstPixelHue < 5 * 65536; firstPixelHue += 256) for (long firstPixelHue = 0; firstPixelHue < 5 * 65536;
{ firstPixelHue += 256) {
// strip.rainbow() can take a single argument (first pixel hue) or // strip.rainbow() can take a single argument (first pixel hue) or
// optionally a few extras: number of rainbow repetitions (default 1), // optionally a few extras: number of rainbow repetitions (default 1),
// saturation and value (brightness) (both 0-255, similar to the // saturation and value (brightness) (both 0-255, similar to the
@ -330,16 +288,12 @@ void ledRainbow(int wait)
} }
} }
void ledTheaterChase(uint32_t color, int wait) void ledTheaterChase(uint32_t color, int wait) {
{ for (int a = 0; a < 10; a++) { // Repeat 10 times...
for (int a = 0; a < 10; a++) for (int b = 0; b < 3; b++) { // 'b' counts from 0 to 2...
{ // Repeat 10 times...
for (int b = 0; b < 3; b++)
{ // 'b' counts from 0 to 2...
pixels.clear(); // Set all pixels in RAM to 0 (off) pixels.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in steps of 3... // 'c' counts up from 'b' to end of strip in steps of 3...
for (int c = b; c < pixels.numPixels(); c += 3) for (int c = b; c < pixels.numPixels(); c += 3) {
{
pixels.setPixelColor(c, color); // Set pixel 'c' to value 'color' pixels.setPixelColor(c, color); // Set pixel 'c' to value 'color'
} }
pixels.show(); // Update strip with new contents pixels.show(); // Update strip with new contents
@ -348,17 +302,13 @@ void ledTheaterChase(uint32_t color, int wait)
} }
} }
void ledTheaterChaseRainbow(int wait) void ledTheaterChaseRainbow(int wait) {
{
int firstPixelHue = 0; // First pixel starts at red (hue 0) int firstPixelHue = 0; // First pixel starts at red (hue 0)
for (int a = 0; a < 30; a++) for (int a = 0; a < 30; a++) { // Repeat 30 times...
{ // Repeat 30 times... for (int b = 0; b < 3; b++) { // 'b' counts from 0 to 2...
for (int b = 0; b < 3; b++)
{ // 'b' counts from 0 to 2...
pixels.clear(); // Set all pixels in RAM to 0 (off) pixels.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in increments of 3... // 'c' counts up from 'b' to end of strip in increments of 3...
for (int c = b; c < pixels.numPixels(); c += 3) for (int c = b; c < pixels.numPixels(); c += 3) {
{
// hue of pixel 'c' is offset by an amount to make one full // hue of pixel 'c' is offset by an amount to make one full
// revolution of the color wheel (range 65536) along the length // revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps): // of the strip (strip.numPixels() steps):
@ -373,7 +323,4 @@ void ledTheaterChaseRainbow(int wait)
} }
} }
Adafruit_NeoPixel getPixels() Adafruit_NeoPixel getPixels() { return pixels; }
{
return pixels;
}

View File

@ -1,11 +1,11 @@
#pragma once #pragma once
#include <Arduino.h> #include "lib/shared.hpp"
#include "lib/webserver.hpp"
#include <Adafruit_NeoPixel.h> #include <Adafruit_NeoPixel.h>
#include <Arduino.h>
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/task.h> #include <freertos/task.h>
#include "webserver.hpp"
#include "shared.hpp"
#ifndef NEOPIXEL_PIN #ifndef NEOPIXEL_PIN
#define NEOPIXEL_PIN 34 #define NEOPIXEL_PIN 34

View File

@ -2,10 +2,8 @@
TaskHandle_t taskOtaHandle = NULL; TaskHandle_t taskOtaHandle = NULL;
void setupOTA() void setupOTA() {
{ if (preferences.getBool("otaEnabled", true)) {
if (preferences.getBool("otaEnabled", true))
{
ArduinoOTA.onStart(onOTAStart); ArduinoOTA.onStart(onOTAStart);
ArduinoOTA.onProgress(onOTAProgress); ArduinoOTA.onProgress(onOTAProgress);
@ -18,37 +16,33 @@ void setupOTA()
ArduinoOTA.begin(); ArduinoOTA.begin();
// downloadUpdate(); // downloadUpdate();
xTaskCreate(handleOTATask, "handleOTA", 4096, NULL, tskIDLE_PRIORITY, &taskOtaHandle); xTaskCreate(handleOTATask, "handleOTA", 4096, NULL, tskIDLE_PRIORITY,
&taskOtaHandle);
} }
} }
void onOTAProgress(unsigned int progress, unsigned int total) void onOTAProgress(unsigned int progress, unsigned int total) {
{
uint percentage = progress / (total / 100); uint percentage = progress / (total / 100);
pixels.fill(pixels.Color(0, 255, 0)); pixels.fill(pixels.Color(0, 255, 0));
if (percentage < 100) if (percentage < 100) {
{
pixels.setPixelColor(0, pixels.Color(0, 0, 0)); pixels.setPixelColor(0, pixels.Color(0, 0, 0));
} }
if (percentage < 75) if (percentage < 75) {
{
pixels.setPixelColor(1, pixels.Color(0, 0, 0)); pixels.setPixelColor(1, pixels.Color(0, 0, 0));
} }
if (percentage < 50) if (percentage < 50) {
{
pixels.setPixelColor(2, pixels.Color(0, 0, 0)); pixels.setPixelColor(2, pixels.Color(0, 0, 0));
} }
if (percentage < 25) if (percentage < 25) {
{
pixels.setPixelColor(3, pixels.Color(0, 0, 0)); pixels.setPixelColor(3, pixels.Color(0, 0, 0));
} }
pixels.show(); pixels.show();
} }
void onOTAStart() void onOTAStart() {
{
forceFullRefresh(); forceFullRefresh();
std::array<String, NUM_SCREENS> epdContent = {"U", "P", "D", "A", "T", "E", "!"}; std::array<String, NUM_SCREENS> epdContent = {"U", "P", "D", "A",
"T", "E", "!"};
setEpdContent(epdContent); setEpdContent(epdContent);
// Stop all timers // Stop all timers
esp_timer_stop(screenRotateTimer); esp_timer_stop(screenRotateTimer);
@ -68,17 +62,14 @@ void onOTAStart()
stopPriceNotify(); stopPriceNotify();
} }
void handleOTATask(void *parameter) void handleOTATask(void *parameter) {
{ for (;;) {
for (;;)
{
ArduinoOTA.handle(); // Allow OTA updates to occur ArduinoOTA.handle(); // Allow OTA updates to occur
vTaskDelay(pdMS_TO_TICKS(2500)); vTaskDelay(pdMS_TO_TICKS(2500));
} }
} }
void downloadUpdate() void downloadUpdate() {
{
WiFiClientSecure client; WiFiClientSecure client;
client.setInsecure(); client.setInsecure();
HTTPClient http; HTTPClient http;
@ -87,11 +78,11 @@ void downloadUpdate()
// Send HTTP request to CoinGecko API // Send HTTP request to CoinGecko API
http.useHTTP10(true); http.useHTTP10(true);
http.begin(client, "https://api.github.com/repos/btclock/btclock_v3/releases/latest"); http.begin(client,
"https://api.github.com/repos/btclock/btclock_v3/releases/latest");
int httpCode = http.GET(); int httpCode = http.GET();
if (httpCode == 200) if (httpCode == 200) {
{
// WiFiClient * stream = http->getStreamPtr(); // WiFiClient * stream = http->getStreamPtr();
StaticJsonDocument<64> filter; StaticJsonDocument<64> filter;
@ -102,18 +93,17 @@ void downloadUpdate()
SpiRamJsonDocument doc(1536); SpiRamJsonDocument doc(1536);
DeserializationError error = deserializeJson(doc, http.getStream(), DeserializationOption::Filter(filter)); DeserializationError error = deserializeJson(
doc, http.getStream(), DeserializationOption::Filter(filter));
if (error) if (error) {
{
Serial.print("deserializeJson() failed: "); Serial.print("deserializeJson() failed: ");
Serial.println(error.c_str()); Serial.println(error.c_str());
return; return;
} }
String downloadUrl; String downloadUrl;
for (JsonObject asset : doc["assets"].as<JsonArray>()) for (JsonObject asset : doc["assets"].as<JsonArray>()) {
{
if (asset["name"].as<String>().compareTo("firmware.bin") == 0) { if (asset["name"].as<String>().compareTo("firmware.bin") == 0) {
downloadUrl = asset["browser_download_url"].as<String>(); downloadUrl = asset["browser_download_url"].as<String>();
break; break;
@ -122,8 +112,6 @@ void downloadUpdate()
Serial.printf("Download update from %s", downloadUrl); Serial.printf("Download update from %s", downloadUrl);
// esp_http_client_config_t config = { // esp_http_client_config_t config = {
// .url = CONFIG_FIRMWARE_UPGRADE_URL, // .url = CONFIG_FIRMWARE_UPGRADE_URL,
// }; // };
@ -138,8 +126,7 @@ void downloadUpdate()
} }
} }
void onOTAError(ota_error_t error) void onOTAError(ota_error_t error) {
{
Serial.println("\nOTA update error, restarting"); Serial.println("\nOTA update error, restarting");
Wire.end(); Wire.end();
SPI.end(); SPI.end();
@ -147,8 +134,7 @@ void onOTAError(ota_error_t error)
ESP.restart(); ESP.restart();
} }
void onOTAComplete() void onOTAComplete() {
{
Serial.println("\nOTA update finished"); Serial.println("\nOTA update finished");
Wire.end(); Wire.end();
SPI.end(); SPI.end();

View File

@ -1,7 +1,7 @@
#include "lib/config.hpp"
#include "lib/shared.hpp"
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoOTA.h> #include <ArduinoOTA.h>
#include "config.hpp"
#include "shared.hpp"
void setupOTA(); void setupOTA();
void onOTAStart(); void onOTAStart();

View File

@ -1,15 +1,14 @@
#include "price_fetch.hpp" #include "price_fetch.hpp"
const PROGMEM char *cgApiUrl = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd%2Ceur"; const PROGMEM char *cgApiUrl = "https://api.coingecko.com/api/v3/simple/"
"price?ids=bitcoin&vs_currencies=usd%2Ceur";
TaskHandle_t priceFetchTaskHandle; TaskHandle_t priceFetchTaskHandle;
void taskPriceFetch(void *pvParameters) void taskPriceFetch(void *pvParameters) {
{
WiFiClientSecure *client = new WiFiClientSecure; WiFiClientSecure *client = new WiFiClientSecure;
client->setInsecure(); client->setInsecure();
for (;;) for (;;) {
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
HTTPClient *http = new HTTPClient(); HTTPClient *http = new HTTPClient();
@ -22,8 +21,7 @@ void taskPriceFetch(void *pvParameters)
// Parse JSON response and extract average price // Parse JSON response and extract average price
uint usdPrice, eurPrice; uint usdPrice, eurPrice;
if (httpCode == 200) if (httpCode == 200) {
{
String payload = http->getString(); String payload = http->getString();
StaticJsonDocument<96> doc; StaticJsonDocument<96> doc;
deserializeJson(doc, payload); deserializeJson(doc, payload);
@ -31,30 +29,28 @@ void taskPriceFetch(void *pvParameters)
eurPrice = doc["bitcoin"]["eur"].as<uint>(); eurPrice = doc["bitcoin"]["eur"].as<uint>();
setPrice(eurPrice); setPrice(eurPrice);
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER || getCurrentScreen() == SCREEN_MSCW_TIME || getCurrentScreen() == SCREEN_MARKET_CAP)) if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER ||
{ getCurrentScreen() == SCREEN_MSCW_TIME ||
getCurrentScreen() == SCREEN_MARKET_CAP)) {
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0}; WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY); xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
} }
preferences.putUInt("lastPrice", eurPrice); preferences.putUInt("lastPrice", eurPrice);
} } else {
else Serial.print(
{ F("Error retrieving BTC/USD price (CoinGecko). HTTP status code: "));
Serial.print(F("Error retrieving BTC/USD price (CoinGecko). HTTP status code: "));
Serial.println(httpCode); Serial.println(httpCode);
if (httpCode == -1) if (httpCode == -1) {
{
WiFi.reconnect(); WiFi.reconnect();
} }
} }
} }
} }
void setupPriceFetchTask() void setupPriceFetchTask() {
{ xTaskCreate(taskPriceFetch, "priceFetch", (6 * 1024), NULL, tskIDLE_PRIORITY,
xTaskCreate(taskPriceFetch, "priceFetch", (6*1024), NULL, tskIDLE_PRIORITY, &priceFetchTaskHandle); &priceFetchTaskHandle);
xTaskNotifyGive(priceFetchTaskHandle); xTaskNotifyGive(priceFetchTaskHandle);
} }

View File

@ -1,7 +1,7 @@
#include "lib/config.hpp"
#include "lib/shared.hpp"
#include <Arduino.h> #include <Arduino.h>
#include <HTTPClient.h> #include <HTTPClient.h>
#include "config.hpp"
#include "shared.hpp"
extern TaskHandle_t priceFetchTaskHandle; extern TaskHandle_t priceFetchTaskHandle;

View File

@ -38,28 +38,25 @@ esp_websocket_client_handle_t clientPrice = NULL;
uint currentPrice = 30000; uint currentPrice = 30000;
unsigned long int lastPriceUpdate; unsigned long int lastPriceUpdate;
void setupPriceNotify() void setupPriceNotify() {
{
// currentPrice = preferences.get("lastPrice", 30000); // currentPrice = preferences.get("lastPrice", 30000);
esp_websocket_client_config_t config = { esp_websocket_client_config_t config = {.uri = wsServerPrice,
.uri = wsServerPrice,
// .task_stack = (7*1024), // .task_stack = (7*1024),
// .cert_pem = coinCapWsCert, // .cert_pem = coinCapWsCert,
.user_agent = USER_AGENT .user_agent = USER_AGENT};
};
clientPrice = esp_websocket_client_init(&config); clientPrice = esp_websocket_client_init(&config);
esp_websocket_register_events(clientPrice, WEBSOCKET_EVENT_ANY, onWebsocketPriceEvent, clientPrice); esp_websocket_register_events(clientPrice, WEBSOCKET_EVENT_ANY,
onWebsocketPriceEvent, clientPrice);
esp_websocket_client_start(clientPrice); esp_websocket_client_start(clientPrice);
} }
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
{ int32_t event_id, void *event_data) {
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data; esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
switch (event_id) switch (event_id) {
{
case WEBSOCKET_EVENT_CONNECTED: case WEBSOCKET_EVENT_CONNECTED:
Serial.println(F("Connected to CoinCap.io WebSocket")); Serial.println(F("Connected to CoinCap.io WebSocket"));
break; break;
@ -75,8 +72,7 @@ void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base, int32_t ev
} }
} }
void onWebsocketPriceMessage(esp_websocket_event_data_t* event_data) void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data) {
{
SpiRamJsonDocument doc(event_data->data_len); SpiRamJsonDocument doc(event_data->data_len);
deserializeJson(doc, (char *)event_data->data_ptr); deserializeJson(doc, (char *)event_data->data_ptr);
@ -84,16 +80,20 @@ void onWebsocketPriceMessage(esp_websocket_event_data_t* event_data)
if (doc.containsKey("bitcoin")) { if (doc.containsKey("bitcoin")) {
if (currentPrice != doc["bitcoin"].as<long>()) { if (currentPrice != doc["bitcoin"].as<long>()) {
uint minSecPriceUpd = preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE); uint minSecPriceUpd = preferences.getUInt(
"minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE);
uint currentTime = esp_timer_get_time() / 1000000; uint currentTime = esp_timer_get_time() / 1000000;
if (lastPriceUpdate == 0 || (currentTime - lastPriceUpdate) > minSecPriceUpd) { if (lastPriceUpdate == 0 ||
(currentTime - lastPriceUpdate) > minSecPriceUpd) {
// const unsigned long oldPrice = currentPrice; // const unsigned long oldPrice = currentPrice;
currentPrice = doc["bitcoin"].as<uint>(); currentPrice = doc["bitcoin"].as<uint>();
preferences.putUInt("lastPrice", currentPrice); preferences.putUInt("lastPrice", currentPrice);
lastPriceUpdate = currentTime; lastPriceUpdate = currentTime;
// if (abs((int)(oldPrice-currentPrice)) > round(0.0015*oldPrice)) { // if (abs((int)(oldPrice-currentPrice)) > round(0.0015*oldPrice)) {
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER || getCurrentScreen() == SCREEN_MSCW_TIME || getCurrentScreen() == SCREEN_MARKET_CAP)) { if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER ||
getCurrentScreen() == SCREEN_MSCW_TIME ||
getCurrentScreen() == SCREEN_MARKET_CAP)) {
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0}; WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY); xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
} }
@ -103,13 +103,9 @@ void onWebsocketPriceMessage(esp_websocket_event_data_t* event_data)
} }
} }
uint getPrice() { uint getPrice() { return currentPrice; }
return currentPrice;
}
void setPrice(uint newPrice) { void setPrice(uint newPrice) { currentPrice = newPrice; }
currentPrice = newPrice;
}
bool isPriceNotifyConnected() { bool isPriceNotifyConnected() {
if (clientPrice == NULL) if (clientPrice == NULL)

View File

@ -1,17 +1,18 @@
#pragma once #pragma once
#include <string>
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <string>
#include "esp_websocket_client.h" #include <esp_websocket_client.h>
#include "screen_handler.hpp" #include "lib/screen_handler.hpp"
// using namespace websockets; // using namespace websockets;
void setupPriceNotify(); void setupPriceNotify();
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data); void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
int32_t event_id, void *event_data);
void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data); void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data);
uint getPrice(); uint getPrice();

View File

@ -8,7 +8,8 @@ TaskHandle_t workerTaskHandle;
esp_timer_handle_t screenRotateTimer; esp_timer_handle_t screenRotateTimer;
esp_timer_handle_t minuteTimer; esp_timer_handle_t minuteTimer;
std::array<std::string, NUM_SCREENS> taskEpdContent = {"", "", "", "", "", "", ""}; std::array<std::string, NUM_SCREENS> taskEpdContent = {"", "", "", "",
"", "", ""};
std::string priceString; std::string priceString;
// typedef enum // typedef enum
@ -29,65 +30,50 @@ QueueHandle_t workQueue = NULL;
uint currentScreen; uint currentScreen;
void workerTask(void *pvParameters) void workerTask(void *pvParameters) {
{
WorkItem receivedItem; WorkItem receivedItem;
while (1) while (1) {
{
// Wait for a work item to be available in the queue // Wait for a work item to be available in the queue
if (xQueueReceive(workQueue, &receivedItem, portMAX_DELAY)) if (xQueueReceive(workQueue, &receivedItem, portMAX_DELAY)) {
{
uint firstIndex = 0; uint firstIndex = 0;
// Process the work item based on its type // Process the work item based on its type
switch (receivedItem.type) switch (receivedItem.type) {
{ case TASK_PRICE_UPDATE: {
case TASK_PRICE_UPDATE:
{
uint price = getPrice(); uint price = getPrice();
char priceSymbol = '$'; char priceSymbol = '$';
if (preferences.getBool("fetchEurPrice", false)) if (preferences.getBool("fetchEurPrice", false)) {
{
priceSymbol = '['; priceSymbol = '[';
} }
if (getCurrentScreen() == SCREEN_BTC_TICKER) if (getCurrentScreen() == SCREEN_BTC_TICKER) {
{
taskEpdContent = parsePriceData(price, priceSymbol); taskEpdContent = parsePriceData(price, priceSymbol);
} } else if (getCurrentScreen() == SCREEN_MSCW_TIME) {
else if (getCurrentScreen() == SCREEN_MSCW_TIME)
{
taskEpdContent = parseSatsPerCurrency(price, priceSymbol); taskEpdContent = parseSatsPerCurrency(price, priceSymbol);
} } else {
else taskEpdContent =
{ parseMarketCap(getBlockHeight(), price, priceSymbol,
taskEpdContent = parseMarketCap(getBlockHeight(), price, priceSymbol, preferences.getBool("mcapBigChar", true)); preferences.getBool("mcapBigChar", true));
} }
setEpdContent(taskEpdContent); setEpdContent(taskEpdContent);
break; break;
} }
case TASK_BLOCK_UPDATE: case TASK_BLOCK_UPDATE: {
{ if (getCurrentScreen() != SCREEN_HALVING_COUNTDOWN) {
if (getCurrentScreen() != SCREEN_HALVING_COUNTDOWN)
{
taskEpdContent = parseBlockHeight(getBlockHeight()); taskEpdContent = parseBlockHeight(getBlockHeight());
} } else {
else
{
taskEpdContent = parseHalvingCountdown(getBlockHeight()); taskEpdContent = parseHalvingCountdown(getBlockHeight());
} }
if (getCurrentScreen() == SCREEN_HALVING_COUNTDOWN || getCurrentScreen() == SCREEN_BLOCK_HEIGHT) if (getCurrentScreen() == SCREEN_HALVING_COUNTDOWN ||
{ getCurrentScreen() == SCREEN_BLOCK_HEIGHT) {
setEpdContent(taskEpdContent); setEpdContent(taskEpdContent);
} }
break; break;
} }
case TASK_TIME_UPDATE: case TASK_TIME_UPDATE: {
{ if (getCurrentScreen() == SCREEN_TIME) {
if (getCurrentScreen() == SCREEN_TIME)
{
time_t currentTime; time_t currentTime;
struct tm timeinfo; struct tm timeinfo;
time(&currentTime); time(&currentTime);
@ -95,17 +81,17 @@ void workerTask(void *pvParameters)
std::string timeString; std::string timeString;
String minute = String(timeinfo.tm_min); String minute = String(timeinfo.tm_min);
if (minute.length() < 2) if (minute.length() < 2) {
{
minute = "0" + minute; minute = "0" + minute;
} }
timeString = std::to_string(timeinfo.tm_hour) + ":" + minute.c_str(); timeString = std::to_string(timeinfo.tm_hour) + ":" + minute.c_str();
timeString.insert(timeString.begin(), NUM_SCREENS - timeString.length(), ' '); timeString.insert(timeString.begin(),
taskEpdContent[0] = std::to_string(timeinfo.tm_mday) + "/" + std::to_string(timeinfo.tm_mon + 1); NUM_SCREENS - timeString.length(), ' ');
taskEpdContent[0] = std::to_string(timeinfo.tm_mday) + "/" +
std::to_string(timeinfo.tm_mon + 1);
for (uint i = 1; i < NUM_SCREENS; i++) for (uint i = 1; i < NUM_SCREENS; i++) {
{
taskEpdContent[i] = timeString[i]; taskEpdContent[i] = timeString[i];
} }
setEpdContent(taskEpdContent); setEpdContent(taskEpdContent);
@ -119,17 +105,14 @@ void workerTask(void *pvParameters)
} }
} }
void taskScreenRotate(void *pvParameters) void taskScreenRotate(void *pvParameters) {
{ for (;;) {
for (;;)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
int nextScreen = (currentScreen + 1) % SCREEN_COUNT; int nextScreen = (currentScreen + 1) % SCREEN_COUNT;
String key = "screen" + String(nextScreen) + "Visible"; String key = "screen" + String(nextScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true)) while (!preferences.getBool(key.c_str(), true)) {
{
nextScreen = (nextScreen + 1) % SCREEN_COUNT; nextScreen = (nextScreen + 1) % SCREEN_COUNT;
key = "screen" + String(nextScreen) + "Visible"; key = "screen" + String(nextScreen) + "Visible";
} }
@ -138,52 +121,48 @@ void taskScreenRotate(void *pvParameters)
} }
} }
void IRAM_ATTR minuteTimerISR(void *arg) void IRAM_ATTR minuteTimerISR(void *arg) {
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// vTaskNotifyGiveFromISR(timeUpdateTaskHandle, &xHigherPriorityTaskWoken); // vTaskNotifyGiveFromISR(timeUpdateTaskHandle, &xHigherPriorityTaskWoken);
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0}; WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
xQueueSendFromISR(workQueue, &timeUpdate, &xHigherPriorityTaskWoken); xQueueSendFromISR(workQueue, &timeUpdate, &xHigherPriorityTaskWoken);
if (priceFetchTaskHandle != NULL) if (priceFetchTaskHandle != NULL) {
{
vTaskNotifyGiveFromISR(priceFetchTaskHandle, &xHigherPriorityTaskWoken); vTaskNotifyGiveFromISR(priceFetchTaskHandle, &xHigherPriorityTaskWoken);
} }
if (xHigherPriorityTaskWoken == pdTRUE) if (xHigherPriorityTaskWoken == pdTRUE) {
{
portYIELD_FROM_ISR(); portYIELD_FROM_ISR();
} }
} }
void IRAM_ATTR screenRotateTimerISR(void *arg) void IRAM_ATTR screenRotateTimerISR(void *arg) {
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(taskScreenRotateTaskHandle, &xHigherPriorityTaskWoken); vTaskNotifyGiveFromISR(taskScreenRotateTaskHandle, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE) if (xHigherPriorityTaskWoken == pdTRUE) {
{
portYIELD_FROM_ISR(); portYIELD_FROM_ISR();
} }
} }
void setupTasks() void setupTasks() {
{
workQueue = xQueueCreate(WORK_QUEUE_SIZE, sizeof(WorkItem)); workQueue = xQueueCreate(WORK_QUEUE_SIZE, sizeof(WorkItem));
// xTaskCreate(taskPriceUpdate, "updatePrice", 1024, NULL, tskIDLE_PRIORITY, &priceUpdateTaskHandle); // xTaskCreate(taskPriceUpdate, "updatePrice", 1024, NULL, tskIDLE_PRIORITY,
// xTaskCreate(taskBlockUpdate, "updateBlock", 1024, NULL, tskIDLE_PRIORITY, &blockUpdateTaskHandle); // &priceUpdateTaskHandle); xTaskCreate(taskBlockUpdate, "updateBlock", 1024,
// xTaskCreate(taskTimeUpdate, "updateTime", 1024, NULL, tskIDLE_PRIORITY, &timeUpdateTaskHandle); // NULL, tskIDLE_PRIORITY, &blockUpdateTaskHandle);
xTaskCreate(workerTask, "workerTask", 4096, NULL, tskIDLE_PRIORITY, &workerTaskHandle); // xTaskCreate(taskTimeUpdate, "updateTime", 1024, NULL, tskIDLE_PRIORITY,
// &timeUpdateTaskHandle);
xTaskCreate(workerTask, "workerTask", 4096, NULL, tskIDLE_PRIORITY,
&workerTaskHandle);
xTaskCreate(taskScreenRotate, "rotateScreen", 2048, NULL, tskIDLE_PRIORITY, &taskScreenRotateTaskHandle); xTaskCreate(taskScreenRotate, "rotateScreen", 2048, NULL, tskIDLE_PRIORITY,
&taskScreenRotateTaskHandle);
waitUntilNoneBusy(); waitUntilNoneBusy();
setCurrentScreen(preferences.getUInt("currentScreen", 0)); setCurrentScreen(preferences.getUInt("currentScreen", 0));
} }
void setupTimeUpdateTimer(void *pvParameters) void setupTimeUpdateTimer(void *pvParameters) {
{
const esp_timer_create_args_t minuteTimerConfig = { const esp_timer_create_args_t minuteTimerConfig = {
.callback = &minuteTimerISR, .callback = &minuteTimerISR, .name = "minute_timer"};
.name = "minute_timer"};
esp_timer_create(&minuteTimerConfig, &minuteTimer); esp_timer_create(&minuteTimerConfig, &minuteTimer);
@ -205,42 +184,31 @@ void setupTimeUpdateTimer(void *pvParameters)
vTaskDelete(NULL); vTaskDelete(NULL);
} }
void setupScreenRotateTimer(void *pvParameters) void setupScreenRotateTimer(void *pvParameters) {
{
const esp_timer_create_args_t screenRotateTimerConfig = { const esp_timer_create_args_t screenRotateTimerConfig = {
.callback = &screenRotateTimerISR, .callback = &screenRotateTimerISR, .name = "screen_rotate_timer"};
.name = "screen_rotate_timer"};
esp_timer_create(&screenRotateTimerConfig, &screenRotateTimer); esp_timer_create(&screenRotateTimerConfig, &screenRotateTimer);
if (preferences.getBool("timerActive", true)) if (preferences.getBool("timerActive", true)) {
{ esp_timer_start_periodic(screenRotateTimer,
esp_timer_start_periodic(screenRotateTimer, getTimerSeconds() * usPerSecond); getTimerSeconds() * usPerSecond);
} }
vTaskDelete(NULL); vTaskDelete(NULL);
} }
uint getTimerSeconds() uint getTimerSeconds() { return preferences.getUInt("timerSeconds", 1800); }
{
return preferences.getUInt("timerSeconds", 1800);
}
bool isTimerActive() bool isTimerActive() { return esp_timer_is_active(screenRotateTimer); }
{
return esp_timer_is_active(screenRotateTimer);
}
void setTimerActive(bool status) void setTimerActive(bool status) {
{ if (status) {
if (status) esp_timer_start_periodic(screenRotateTimer,
{ getTimerSeconds() * usPerSecond);
esp_timer_start_periodic(screenRotateTimer, getTimerSeconds() * usPerSecond);
queueLedEffect(LED_EFFECT_START_TIMER); queueLedEffect(LED_EFFECT_START_TIMER);
preferences.putBool("timerActive", true); preferences.putBool("timerActive", true);
} } else {
else
{
esp_timer_stop(screenRotateTimer); esp_timer_stop(screenRotateTimer);
queueLedEffect(LED_EFFECT_PAUSE_TIMER); queueLedEffect(LED_EFFECT_PAUSE_TIMER);
preferences.putBool("timerActive", false); preferences.putBool("timerActive", false);
@ -250,37 +218,26 @@ void setTimerActive(bool status)
xTaskNotifyGive(eventSourceTaskHandle); xTaskNotifyGive(eventSourceTaskHandle);
} }
void toggleTimerActive() void toggleTimerActive() { setTimerActive(!isTimerActive()); }
{
setTimerActive(!isTimerActive());
}
uint getCurrentScreen() uint getCurrentScreen() { return currentScreen; }
{
return currentScreen;
}
void setCurrentScreen(uint newScreen) void setCurrentScreen(uint newScreen) {
{ if (newScreen != SCREEN_CUSTOM) {
if (newScreen != SCREEN_CUSTOM)
{
preferences.putUInt("currentScreen", newScreen); preferences.putUInt("currentScreen", newScreen);
} }
currentScreen = newScreen; currentScreen = newScreen;
switch (currentScreen) switch (currentScreen) {
{ case SCREEN_TIME: {
case SCREEN_TIME:
{
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0}; WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
xQueueSend(workQueue, &timeUpdate, portMAX_DELAY); xQueueSend(workQueue, &timeUpdate, portMAX_DELAY);
// xTaskNotifyGive(timeUpdateTaskHandle); // xTaskNotifyGive(timeUpdateTaskHandle);
break; break;
} }
case SCREEN_HALVING_COUNTDOWN: case SCREEN_HALVING_COUNTDOWN:
case SCREEN_BLOCK_HEIGHT: case SCREEN_BLOCK_HEIGHT: {
{
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0}; WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
// xTaskNotifyGive(blockUpdateTaskHandle); // xTaskNotifyGive(blockUpdateTaskHandle);
@ -288,8 +245,7 @@ void setCurrentScreen(uint newScreen)
} }
case SCREEN_MARKET_CAP: case SCREEN_MARKET_CAP:
case SCREEN_MSCW_TIME: case SCREEN_MSCW_TIME:
case SCREEN_BTC_TICKER: case SCREEN_BTC_TICKER: {
{
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0}; WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY); xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
// xTaskNotifyGive(priceUpdateTaskHandle); // xTaskNotifyGive(priceUpdateTaskHandle);
@ -301,36 +257,32 @@ void setCurrentScreen(uint newScreen)
xTaskNotifyGive(eventSourceTaskHandle); xTaskNotifyGive(eventSourceTaskHandle);
} }
void nextScreen() void nextScreen() {
{
int newCurrentScreen = (getCurrentScreen() + 1) % SCREEN_COUNT; int newCurrentScreen = (getCurrentScreen() + 1) % SCREEN_COUNT;
String key = "screen" + String(newCurrentScreen) + "Visible"; String key = "screen" + String(newCurrentScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true)) while (!preferences.getBool(key.c_str(), true)) {
{
newCurrentScreen = (newCurrentScreen + 1) % SCREEN_COUNT; newCurrentScreen = (newCurrentScreen + 1) % SCREEN_COUNT;
key = "screen" + String(newCurrentScreen) + "Visible"; key = "screen" + String(newCurrentScreen) + "Visible";
} }
setCurrentScreen(newCurrentScreen); setCurrentScreen(newCurrentScreen);
} }
void previousScreen() void previousScreen() {
{
int newCurrentScreen = modulo(getCurrentScreen() - 1, SCREEN_COUNT); int newCurrentScreen = modulo(getCurrentScreen() - 1, SCREEN_COUNT);
String key = "screen" + String(newCurrentScreen) + "Visible"; String key = "screen" + String(newCurrentScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true)) while (!preferences.getBool(key.c_str(), true)) {
{
newCurrentScreen = modulo(newCurrentScreen - 1, SCREEN_COUNT); newCurrentScreen = modulo(newCurrentScreen - 1, SCREEN_COUNT);
key = "screen" + String(newCurrentScreen) + "Visible"; key = "screen" + String(newCurrentScreen) + "Visible";
} }
setCurrentScreen(newCurrentScreen); setCurrentScreen(newCurrentScreen);
} }
void showSystemStatusScreen() void showSystemStatusScreen() {
{ std::array<String, NUM_SCREENS> sysStatusEpdContent = {"", "", "", "",
std::array<String, NUM_SCREENS> sysStatusEpdContent = {"", "", "", "", "", "", ""}; "", "", ""};
String ipAddr = WiFi.localIP().toString(); String ipAddr = WiFi.localIP().toString();
String subNet = WiFi.subnetMask().toString(); String subNet = WiFi.subnetMask().toString();
@ -339,9 +291,9 @@ void showSystemStatusScreen()
int ipAddrPos = 0; int ipAddrPos = 0;
int subnetPos = 0; int subnetPos = 0;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++) {
{ sysStatusEpdContent[1 + i] = ipAddr.substring(0, ipAddr.indexOf('.')) +
sysStatusEpdContent[1 + i] = ipAddr.substring(0, ipAddr.indexOf('.')) + "/" + subNet.substring(0, subNet.indexOf('.')); "/" + subNet.substring(0, subNet.indexOf('.'));
ipAddrPos = ipAddr.indexOf('.') + 1; ipAddrPos = ipAddr.indexOf('.') + 1;
subnetPos = subNet.indexOf('.') + 1; subnetPos = subNet.indexOf('.') + 1;
ipAddr = ipAddr.substring(ipAddrPos); ipAddr = ipAddr.substring(ipAddrPos);
@ -349,7 +301,9 @@ void showSystemStatusScreen()
} }
sysStatusEpdContent[NUM_SCREENS - 2] = "RAM/Status"; sysStatusEpdContent[NUM_SCREENS - 2] = "RAM/Status";
sysStatusEpdContent[NUM_SCREENS - 1] = String((int)round(ESP.getFreeHeap() / 1024)) + "/" + (int)round(ESP.getHeapSize() / 1024); sysStatusEpdContent[NUM_SCREENS - 1] =
String((int)round(ESP.getFreeHeap() / 1024)) + "/" +
(int)round(ESP.getHeapSize() / 1024);
setCurrentScreen(SCREEN_CUSTOM); setCurrentScreen(SCREEN_CUSTOM);
setEpdContent(sysStatusEpdContent); setEpdContent(sysStatusEpdContent);
} }

View File

@ -2,13 +2,13 @@
#include <esp_timer.h> #include <esp_timer.h>
#include <data_handler.hpp>
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/task.h> #include <freertos/task.h>
#include <data_handler.hpp>
#include "price_fetch.hpp"
#include "shared.hpp"
#include "lib/epd.hpp" #include "lib/epd.hpp"
#include "lib/price_fetch.hpp"
#include "lib/shared.hpp"
// extern TaskHandle_t priceUpdateTaskHandle; // extern TaskHandle_t priceUpdateTaskHandle;
// extern TaskHandle_t blockUpdateTaskHandle; // extern TaskHandle_t blockUpdateTaskHandle;
@ -21,15 +21,13 @@ extern esp_timer_handle_t minuteTimer;
extern QueueHandle_t workQueue; extern QueueHandle_t workQueue;
typedef enum typedef enum {
{
TASK_PRICE_UPDATE, TASK_PRICE_UPDATE,
TASK_BLOCK_UPDATE, TASK_BLOCK_UPDATE,
TASK_TIME_UPDATE TASK_TIME_UPDATE
} TaskType; } TaskType;
typedef struct typedef struct {
{
TaskType type; TaskType type;
char data; char data;
} WorkItem; } WorkItem;

View File

@ -1,13 +1,13 @@
#pragma once #pragma once
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <Adafruit_MCP23X17.h> #include <Adafruit_MCP23X17.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <Preferences.h> #include <Preferences.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <mutex> #include <mutex>
#include "utils.hpp" #include <utils.hpp>
extern Adafruit_MCP23X17 mcp1; extern Adafruit_MCP23X17 mcp1;
#ifdef IS_BTCLOCK_S3 #ifdef IS_BTCLOCK_S3
@ -26,7 +26,9 @@ const PROGMEM int SCREEN_MARKET_CAP = 5;
const PROGMEM int SCREEN_COUNTDOWN = 98; const PROGMEM int SCREEN_COUNTDOWN = 98;
const PROGMEM int SCREEN_CUSTOM = 99; const PROGMEM int SCREEN_CUSTOM = 99;
const int SCREEN_COUNT = 6; const int SCREEN_COUNT = 6;
const PROGMEM int screens[SCREEN_COUNT] = { SCREEN_BLOCK_HEIGHT, SCREEN_MSCW_TIME, SCREEN_BTC_TICKER, SCREEN_TIME, SCREEN_HALVING_COUNTDOWN, SCREEN_MARKET_CAP }; const PROGMEM int screens[SCREEN_COUNT] = {
SCREEN_BLOCK_HEIGHT, SCREEN_MSCW_TIME, SCREEN_BTC_TICKER,
SCREEN_TIME, SCREEN_HALVING_COUNTDOWN, SCREEN_MARKET_CAP};
const int usPerSecond = 1000000; const int usPerSecond = 1000000;
const int usPerMinute = 60 * usPerSecond; const int usPerMinute = 60 * usPerSecond;
@ -35,9 +37,7 @@ struct SpiRamAllocator {
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM); return heap_caps_malloc(size, MALLOC_CAP_SPIRAM);
} }
void deallocate(void* pointer) { void deallocate(void *pointer) { heap_caps_free(pointer); }
heap_caps_free(pointer);
}
void *reallocate(void *ptr, size_t new_size) { void *reallocate(void *ptr, size_t new_size) {
return heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM); return heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM);
@ -45,4 +45,3 @@ struct SpiRamAllocator {
}; };
using SpiRamJsonDocument = BasicJsonDocument<SpiRamAllocator>; using SpiRamJsonDocument = BasicJsonDocument<SpiRamAllocator>;

View File

@ -4,11 +4,11 @@ AsyncWebServer server(80);
AsyncEventSource events("/events"); AsyncEventSource events("/events");
TaskHandle_t eventSourceTaskHandle; TaskHandle_t eventSourceTaskHandle;
void setupWebserver() void setupWebserver() {
{
events.onConnect([](AsyncEventSourceClient *client) events.onConnect([](AsyncEventSourceClient *client) {
{ client->send("welcome", NULL, millis(), 1000); }); client->send("welcome", NULL, millis(), 1000);
});
server.addHandler(&events); server.addHandler(&events);
// server.serveStatic("/css", LittleFS, "/css/"); // server.serveStatic("/css", LittleFS, "/css/");
@ -34,42 +34,48 @@ void setupWebserver()
server.on("/api/show/screen", HTTP_GET, onApiShowScreen); server.on("/api/show/screen", HTTP_GET, onApiShowScreen);
server.on("/api/show/text", HTTP_GET, onApiShowText); server.on("/api/show/text", HTTP_GET, onApiShowText);
AsyncCallbackJsonWebHandler *settingsPatchHandler = new AsyncCallbackJsonWebHandler("/api/json/settings", onApiSettingsPatch); AsyncCallbackJsonWebHandler *settingsPatchHandler =
new AsyncCallbackJsonWebHandler("/api/json/settings", onApiSettingsPatch);
server.addHandler(settingsPatchHandler); server.addHandler(settingsPatchHandler);
AsyncCallbackJsonWebHandler *handler = new AsyncCallbackJsonWebHandler("/api/show/custom", onApiShowTextAdvanced); AsyncCallbackJsonWebHandler *handler = new AsyncCallbackJsonWebHandler(
"/api/show/custom", onApiShowTextAdvanced);
server.addHandler(handler); server.addHandler(handler);
AsyncCallbackJsonWebHandler *lightsJsonHandler = new AsyncCallbackJsonWebHandler("/api/lights", onApiLightsSetJson); AsyncCallbackJsonWebHandler *lightsJsonHandler =
new AsyncCallbackJsonWebHandler("/api/lights", onApiLightsSetJson);
server.addHandler(lightsJsonHandler); server.addHandler(lightsJsonHandler);
server.on("/api/lights/off", HTTP_GET, onApiLightsOff); server.on("/api/lights/off", HTTP_GET, onApiLightsOff);
server.on("/api/lights/color", HTTP_GET, onApiLightsSetColor); server.on("/api/lights/color", HTTP_GET, onApiLightsSetColor);
server.on("/api/lights", HTTP_GET, onApiLightsStatus); server.on("/api/lights", HTTP_GET, onApiLightsStatus);
// server.on("^\\/api\\/lights\\/([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", HTTP_GET, onApiLightsSetColor); // server.on("^\\/api\\/lights\\/([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", HTTP_GET,
// onApiLightsSetColor);
server.on("/api/restart", HTTP_GET, onApiRestart); server.on("/api/restart", HTTP_GET, onApiRestart);
server.addRewrite(new OneParamRewrite("/api/lights/{color}", "/api/lights/color?c={color}")); server.addRewrite(new OneParamRewrite("/api/lights/{color}",
server.addRewrite(new OneParamRewrite("/api/show/screen/{s}", "/api/show/screen?s={s}")); "/api/lights/color?c={color}"));
server.addRewrite(new OneParamRewrite("/api/show/text/{text}", "/api/show/text?t={text}")); server.addRewrite(
server.addRewrite(new OneParamRewrite("/api/show/number/{number}", "/api/show/text?t={text}")); new OneParamRewrite("/api/show/screen/{s}", "/api/show/screen?s={s}"));
server.addRewrite(
new OneParamRewrite("/api/show/text/{text}", "/api/show/text?t={text}"));
server.addRewrite(new OneParamRewrite("/api/show/number/{number}",
"/api/show/text?t={text}"));
server.onNotFound(onNotFound); server.onNotFound(onNotFound);
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Methods", "GET, PATCH, POST, OPTIONS"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Methods",
"GET, PATCH, POST, OPTIONS");
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "*"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "*");
server.begin(); server.begin();
if (preferences.getBool("mdnsEnabled", true)) if (preferences.getBool("mdnsEnabled", true)) {
{ if (!MDNS.begin(getMyHostname())) {
if (!MDNS.begin(getMyHostname()))
{
Serial.println(F("Error setting up MDNS responder!")); Serial.println(F("Error setting up MDNS responder!"));
while (1) while (1) {
{
delay(1000); delay(1000);
} }
} }
@ -79,16 +85,13 @@ void setupWebserver()
MDNS.addServiceTxt("http", "tcp", "rev", GIT_REV); MDNS.addServiceTxt("http", "tcp", "rev", GIT_REV);
} }
xTaskCreate(eventSourceTask, "eventSourceTask", 4096, NULL, tskIDLE_PRIORITY, &eventSourceTaskHandle); xTaskCreate(eventSourceTask, "eventSourceTask", 4096, NULL, tskIDLE_PRIORITY,
&eventSourceTaskHandle);
} }
void stopWebServer() void stopWebServer() { server.end(); }
{
server.end();
}
StaticJsonDocument<768> getStatusObject() StaticJsonDocument<768> getStatusObject() {
{
StaticJsonDocument<768> root; StaticJsonDocument<768> root;
root["currentScreen"] = getCurrentScreen(); root["currentScreen"] = getCurrentScreen();
@ -111,14 +114,12 @@ StaticJsonDocument<768> getStatusObject()
return root; return root;
} }
StaticJsonDocument<512> getLedStatusObject() StaticJsonDocument<512> getLedStatusObject() {
{
StaticJsonDocument<512> root; StaticJsonDocument<512> root;
JsonArray colors = root.createNestedArray("data"); JsonArray colors = root.createNestedArray("data");
// Adafruit_NeoPixel pix = getPixels(); // Adafruit_NeoPixel pix = getPixels();
for (uint i = 0; i < pixels.numPixels(); i++) for (uint i = 0; i < pixels.numPixels(); i++) {
{
uint32_t pixColor = pixels.getPixelColor(pixels.numPixels() - i - 1); uint32_t pixColor = pixels.getPixelColor(pixels.numPixels() - i - 1);
uint alpha = (pixColor >> 24) & 0xFF; uint alpha = (pixColor >> 24) & 0xFF;
uint red = (pixColor >> 16) & 0xFF; uint red = (pixColor >> 16) & 0xFF;
@ -137,8 +138,7 @@ StaticJsonDocument<512> getLedStatusObject()
return root; return root;
} }
void eventSourceUpdate() void eventSourceUpdate() {
{
if (!events.count()) if (!events.count())
return; return;
StaticJsonDocument<768> root = getStatusObject(); StaticJsonDocument<768> root = getStatusObject();
@ -162,9 +162,9 @@ void eventSourceUpdate()
* @Api * @Api
* @Path("/api/status") * @Path("/api/status")
*/ */
void onApiStatus(AsyncWebServerRequest *request) void onApiStatus(AsyncWebServerRequest *request) {
{ AsyncResponseStream *response =
AsyncResponseStream *response = request->beginResponseStream("application/json"); request->beginResponseStream("application/json");
StaticJsonDocument<1024> root = getStatusObject(); StaticJsonDocument<1024> root = getStatusObject();
JsonArray data = root.createNestedArray("data"); JsonArray data = root.createNestedArray("data");
@ -188,8 +188,7 @@ void onApiStatus(AsyncWebServerRequest *request)
* @Api * @Api
* @Path("/api/action/pause") * @Path("/api/action/pause")
*/ */
void onApiActionPause(AsyncWebServerRequest *request) void onApiActionPause(AsyncWebServerRequest *request) {
{
setTimerActive(false); setTimerActive(false);
request->send(200); request->send(200);
}; };
@ -198,8 +197,7 @@ void onApiActionPause(AsyncWebServerRequest *request)
* @Api * @Api
* @Path("/api/action/timer_restart") * @Path("/api/action/timer_restart")
*/ */
void onApiActionTimerRestart(AsyncWebServerRequest *request) void onApiActionTimerRestart(AsyncWebServerRequest *request) {
{
setTimerActive(true); setTimerActive(true);
request->send(200); request->send(200);
} }
@ -208,8 +206,7 @@ void onApiActionTimerRestart(AsyncWebServerRequest *request)
* @Api * @Api
* @Path("/api/full_refresh") * @Path("/api/full_refresh")
*/ */
void onApiFullRefresh(AsyncWebServerRequest *request) void onApiFullRefresh(AsyncWebServerRequest *request) {
{
forceFullRefresh(); forceFullRefresh();
std::array<String, NUM_SCREENS> newEpdContent = getCurrentEpdContent(); std::array<String, NUM_SCREENS> newEpdContent = getCurrentEpdContent();
@ -222,10 +219,8 @@ void onApiFullRefresh(AsyncWebServerRequest *request)
* @Api * @Api
* @Path("/api/show/screen") * @Path("/api/show/screen")
*/ */
void onApiShowScreen(AsyncWebServerRequest *request) void onApiShowScreen(AsyncWebServerRequest *request) {
{ if (request->hasParam("s")) {
if (request->hasParam("s"))
{
AsyncWebParameter *p = request->getParam("s"); AsyncWebParameter *p = request->getParam("s");
uint currentScreen = p->value().toInt(); uint currentScreen = p->value().toInt();
setCurrentScreen(currentScreen); setCurrentScreen(currentScreen);
@ -233,17 +228,14 @@ void onApiShowScreen(AsyncWebServerRequest *request)
request->send(200); request->send(200);
} }
void onApiShowText(AsyncWebServerRequest *request) void onApiShowText(AsyncWebServerRequest *request) {
{ if (request->hasParam("t")) {
if (request->hasParam("t"))
{
AsyncWebParameter *p = request->getParam("t"); AsyncWebParameter *p = request->getParam("t");
String t = p->value(); String t = p->value();
t.toUpperCase(); // This is needed as long as lowercase letters are glitchy t.toUpperCase(); // This is needed as long as lowercase letters are glitchy
std::array<String, NUM_SCREENS> textEpdContent; std::array<String, NUM_SCREENS> textEpdContent;
for (uint i = 0; i < NUM_SCREENS; i++) for (uint i = 0; i < NUM_SCREENS; i++) {
{
textEpdContent[i] = t[i]; textEpdContent[i] = t[i];
} }
@ -253,14 +245,12 @@ void onApiShowText(AsyncWebServerRequest *request)
request->send(200); request->send(200);
} }
void onApiShowTextAdvanced(AsyncWebServerRequest *request, JsonVariant &json) void onApiShowTextAdvanced(AsyncWebServerRequest *request, JsonVariant &json) {
{
JsonArray screens = json.as<JsonArray>(); JsonArray screens = json.as<JsonArray>();
std::array<String, NUM_SCREENS> epdContent; std::array<String, NUM_SCREENS> epdContent;
int i = 0; int i = 0;
for (JsonVariant s : screens) for (JsonVariant s : screens) {
{
epdContent[i] = s.as<String>(); epdContent[i] = s.as<String>();
i++; i++;
} }
@ -271,14 +261,12 @@ void onApiShowTextAdvanced(AsyncWebServerRequest *request, JsonVariant &json)
request->send(200); request->send(200);
} }
void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json) void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json) {
{
JsonObject settings = json.as<JsonObject>(); JsonObject settings = json.as<JsonObject>();
bool settingsChanged = true; bool settingsChanged = true;
if (settings.containsKey("fgColor")) if (settings.containsKey("fgColor")) {
{
String fgColor = settings["fgColor"].as<String>(); String fgColor = settings["fgColor"].as<String>();
preferences.putUInt("fgColor", strtol(fgColor.c_str(), NULL, 16)); preferences.putUInt("fgColor", strtol(fgColor.c_str(), NULL, 16));
setFgColor(int(strtol(fgColor.c_str(), NULL, 16))); setFgColor(int(strtol(fgColor.c_str(), NULL, 16)));
@ -286,8 +274,7 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
Serial.println(strtol(fgColor.c_str(), NULL, 16)); Serial.println(strtol(fgColor.c_str(), NULL, 16));
settingsChanged = true; settingsChanged = true;
} }
if (settings.containsKey("bgColor")) if (settings.containsKey("bgColor")) {
{
String bgColor = settings["bgColor"].as<String>(); String bgColor = settings["bgColor"].as<String>();
preferences.putUInt("bgColor", strtol(bgColor.c_str(), NULL, 16)); preferences.putUInt("bgColor", strtol(bgColor.c_str(), NULL, 16));
@ -297,56 +284,52 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
settingsChanged = true; settingsChanged = true;
} }
if (settings.containsKey("timePerScreen")) if (settings.containsKey("timePerScreen")) {
{ preferences.putUInt("timerSeconds",
preferences.putUInt("timerSeconds", settings["timePerScreen"].as<uint>() * 60); settings["timePerScreen"].as<uint>() * 60);
} }
String strSettings[] = {"hostnamePrefix", "mempoolInstance"}; String strSettings[] = {"hostnamePrefix", "mempoolInstance"};
for (String setting : strSettings) for (String setting : strSettings) {
{ if (settings.containsKey(setting)) {
if (settings.containsKey(setting))
{
preferences.putString(setting.c_str(), settings[setting].as<String>()); preferences.putString(setting.c_str(), settings[setting].as<String>());
Serial.printf("Setting %s to %s\r\n", setting.c_str(), settings[setting].as<String>()); Serial.printf("Setting %s to %s\r\n", setting.c_str(),
settings[setting].as<String>());
} }
} }
String uintSettings[] = {"minSecPriceUpd", "fullRefreshMin", "ledBrightness"}; String uintSettings[] = {"minSecPriceUpd", "fullRefreshMin", "ledBrightness"};
for (String setting : uintSettings) for (String setting : uintSettings) {
{ if (settings.containsKey(setting)) {
if (settings.containsKey(setting))
{
preferences.putUInt(setting.c_str(), settings[setting].as<uint>()); preferences.putUInt(setting.c_str(), settings[setting].as<uint>());
Serial.printf("Setting %s to %d\r\n", setting.c_str(), settings[setting].as<uint>()); Serial.printf("Setting %s to %d\r\n", setting.c_str(),
settings[setting].as<uint>());
} }
} }
if (settings.containsKey("tzOffset")) {
if (settings.containsKey("tzOffset"))
{
int gmtOffset = settings["tzOffset"].as<int>() * 60; int gmtOffset = settings["tzOffset"].as<int>() * 60;
size_t written = preferences.putInt("gmtOffset", gmtOffset); size_t written = preferences.putInt("gmtOffset", gmtOffset);
Serial.printf("Setting %s to %d (%d minutes, written %d)\r\n", "gmtOffset", gmtOffset, settings["tzOffset"].as<int>(), written); Serial.printf("Setting %s to %d (%d minutes, written %d)\r\n", "gmtOffset",
gmtOffset, settings["tzOffset"].as<int>(), written);
} }
String boolSettings[] = {"fetchEurPrice", "ledTestOnPower", "ledFlashOnUpd", "mdnsEnabled", "otaEnabled", "stealFocus", "mcapBigChar"}; String boolSettings[] = {"fetchEurPrice", "ledTestOnPower", "ledFlashOnUpd",
"mdnsEnabled", "otaEnabled", "stealFocus",
"mcapBigChar"};
for (String setting : boolSettings) for (String setting : boolSettings) {
{ if (settings.containsKey(setting)) {
if (settings.containsKey(setting))
{
preferences.putBool(setting.c_str(), settings[setting].as<boolean>()); preferences.putBool(setting.c_str(), settings[setting].as<boolean>());
Serial.printf("Setting %s to %d\r\n", setting.c_str(), settings[setting].as<boolean>()); Serial.printf("Setting %s to %d\r\n", setting.c_str(),
settings[setting].as<boolean>());
} }
} }
if (settings.containsKey("screens")) if (settings.containsKey("screens")) {
{ for (JsonVariant screen : settings["screens"].as<JsonArray>()) {
for (JsonVariant screen : settings["screens"].as<JsonArray>())
{
JsonObject s = screen.as<JsonObject>(); JsonObject s = screen.as<JsonObject>();
uint id = s["id"].as<uint>(); uint id = s["id"].as<uint>();
String key = "screen[" + String(id) + "]"; String key = "screen[" + String(id) + "]";
@ -364,11 +347,11 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
if (WiFi.getTxPower() != 80) { if (WiFi.getTxPower() != 80) {
ESP.restart(); ESP.restart();
} }
} else if (static_cast<int>(wifi_power_t::WIFI_POWER_MINUS_1dBm) <= txPower && } else if (static_cast<int>(wifi_power_t::WIFI_POWER_MINUS_1dBm) <=
txPower &&
txPower <= static_cast<int>(wifi_power_t::WIFI_POWER_19_5dBm)) { txPower <= static_cast<int>(wifi_power_t::WIFI_POWER_19_5dBm)) {
// is valid value // is valid value
if (WiFi.setTxPower(static_cast<wifi_power_t>(txPower))) { if (WiFi.setTxPower(static_cast<wifi_power_t>(txPower))) {
Serial.printf("Set WiFi Tx power to: %d\n", txPower); Serial.printf("Set WiFi Tx power to: %d\n", txPower);
preferences.putInt("txPower", txPower); preferences.putInt("txPower", txPower);
@ -378,14 +361,12 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
} }
request->send(200); request->send(200);
if (settingsChanged) if (settingsChanged) {
{
queueLedEffect(LED_FLASH_SUCCESS); queueLedEffect(LED_FLASH_SUCCESS);
} }
} }
void onApiRestart(AsyncWebServerRequest *request) void onApiRestart(AsyncWebServerRequest *request) {
{
request->send(200); request->send(200);
if (events.count()) if (events.count())
@ -401,20 +382,22 @@ void onApiRestart(AsyncWebServerRequest *request)
* @Method GET * @Method GET
* @Path("/api/settings") * @Path("/api/settings")
*/ */
void onApiSettingsGet(AsyncWebServerRequest *request) void onApiSettingsGet(AsyncWebServerRequest *request) {
{
StaticJsonDocument<1536> root; StaticJsonDocument<1536> root;
root["numScreens"] = NUM_SCREENS; root["numScreens"] = NUM_SCREENS;
root["fgColor"] = getFgColor(); root["fgColor"] = getFgColor();
root["bgColor"] = getBgColor(); root["bgColor"] = getBgColor();
root["timerSeconds"] = getTimerSeconds(); root["timerSeconds"] = getTimerSeconds();
root["timerRunning"] = isTimerActive(); root["timerRunning"] = isTimerActive();
root["minSecPriceUpd"] = preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE); root["minSecPriceUpd"] = preferences.getUInt(
root["fullRefreshMin"] = preferences.getUInt("fullRefreshMin", DEFAULT_MINUTES_FULL_REFRESH); "minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE);
root["fullRefreshMin"] =
preferences.getUInt("fullRefreshMin", DEFAULT_MINUTES_FULL_REFRESH);
root["wpTimeout"] = preferences.getUInt("wpTimeout", 600); root["wpTimeout"] = preferences.getUInt("wpTimeout", 600);
root["tzOffset"] = preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS) / 60; root["tzOffset"] = preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS) / 60;
root["useBitcoinNode"] = preferences.getBool("useNode", false); root["useBitcoinNode"] = preferences.getBool("useNode", false);
root["mempoolInstance"] = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE); root["mempoolInstance"] =
preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
root["ledTestOnPower"] = preferences.getBool("ledTestOnPower", true); root["ledTestOnPower"] = preferences.getBool("ledTestOnPower", true);
root["ledFlashOnUpd"] = preferences.getBool("ledFlashOnUpd", false); root["ledFlashOnUpd"] = preferences.getBool("ledFlashOnUpd", false);
root["ledBrightness"] = preferences.getUInt("ledBrightness", 128); root["ledBrightness"] = preferences.getUInt("ledBrightness", 128);
@ -438,8 +421,7 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
std::vector<std::string> screenNameMap = getScreenNameMap(); std::vector<std::string> screenNameMap = getScreenNameMap();
for (int i = 0; i < screenNameMap.size(); i++) for (int i = 0; i < screenNameMap.size(); i++) {
{
JsonObject o = screens.createNestedObject(); JsonObject o = screens.createNestedObject();
String key = "screen" + String(i) + "Visible"; String key = "screen" + String(i) + "Visible";
o["id"] = i; o["id"] = i;
@ -447,17 +429,16 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
o["enabled"] = preferences.getBool(key.c_str(), true); o["enabled"] = preferences.getBool(key.c_str(), true);
} }
AsyncResponseStream *response = request->beginResponseStream("application/json"); AsyncResponseStream *response =
request->beginResponseStream("application/json");
serializeJson(root, *response); serializeJson(root, *response);
request->send(response); request->send(response);
} }
bool processEpdColorSettings(AsyncWebServerRequest *request) bool processEpdColorSettings(AsyncWebServerRequest *request) {
{
bool settingsChanged = false; bool settingsChanged = false;
if (request->hasParam("fgColor", true)) if (request->hasParam("fgColor", true)) {
{
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)));
@ -465,8 +446,7 @@ bool processEpdColorSettings(AsyncWebServerRequest *request)
// Serial.println(fgColor->value().c_str()); // Serial.println(fgColor->value().c_str());
settingsChanged = true; settingsChanged = true;
} }
if (request->hasParam("bgColor", true)) if (request->hasParam("bgColor", true)) {
{
AsyncWebParameter *bgColor = request->getParam("bgColor", true); AsyncWebParameter *bgColor = request->getParam("bgColor", true);
preferences.putUInt("bgColor", strtol(bgColor->value().c_str(), NULL, 16)); preferences.putUInt("bgColor", strtol(bgColor->value().c_str(), NULL, 16));
@ -479,163 +459,136 @@ bool processEpdColorSettings(AsyncWebServerRequest *request)
return settingsChanged; return settingsChanged;
} }
void onApiSettingsPost(AsyncWebServerRequest *request) void onApiSettingsPost(AsyncWebServerRequest *request) {
{
bool settingsChanged = false; bool settingsChanged = false;
settingsChanged = processEpdColorSettings(request); settingsChanged = processEpdColorSettings(request);
int headers = request->headers(); int headers = request->headers();
int i; int i;
for (i = 0; i < headers; i++) for (i = 0; i < headers; i++) {
{
AsyncWebHeader *h = request->getHeader(i); AsyncWebHeader *h = request->getHeader(i);
Serial.printf("HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); Serial.printf("HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str());
} }
int params = request->params(); int params = request->params();
for (int i = 0; i < params; i++) for (int i = 0; i < params; i++) {
{
AsyncWebParameter *p = request->getParam(i); AsyncWebParameter *p = request->getParam(i);
if (p->isFile()) if (p->isFile()) { // p->isPost() is also true
{ // p->isPost() is also true Serial.printf("FILE[%s]: %s, size: %u\n", p->name().c_str(),
Serial.printf("FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); p->value().c_str(), p->size());
} } else if (p->isPost()) {
else if (p->isPost())
{
Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
} } else {
else
{
Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
} }
} }
if (request->hasParam("fetchEurPrice", true)) if (request->hasParam("fetchEurPrice", true)) {
{
AsyncWebParameter *fetchEurPrice = request->getParam("fetchEurPrice", true); AsyncWebParameter *fetchEurPrice = request->getParam("fetchEurPrice", true);
preferences.putBool("fetchEurPrice", fetchEurPrice->value().toInt()); preferences.putBool("fetchEurPrice", fetchEurPrice->value().toInt());
settingsChanged = true; settingsChanged = true;
} } else {
else
{
preferences.putBool("fetchEurPrice", 0); preferences.putBool("fetchEurPrice", 0);
settingsChanged = true; settingsChanged = true;
} }
if (request->hasParam("ledTestOnPower", true)) if (request->hasParam("ledTestOnPower", true)) {
{ AsyncWebParameter *ledTestOnPower =
AsyncWebParameter *ledTestOnPower = request->getParam("ledTestOnPower", true); request->getParam("ledTestOnPower", true);
preferences.putBool("ledTestOnPower", ledTestOnPower->value().toInt()); preferences.putBool("ledTestOnPower", ledTestOnPower->value().toInt());
settingsChanged = true; settingsChanged = true;
} } else {
else
{
preferences.putBool("ledTestOnPower", 0); preferences.putBool("ledTestOnPower", 0);
settingsChanged = true; settingsChanged = true;
} }
if (request->hasParam("ledFlashOnUpd", true)) if (request->hasParam("ledFlashOnUpd", true)) {
{ AsyncWebParameter *ledFlashOnUpdate =
AsyncWebParameter *ledFlashOnUpdate = request->getParam("ledFlashOnUpd", true); request->getParam("ledFlashOnUpd", true);
preferences.putBool("ledFlashOnUpd", ledFlashOnUpdate->value().toInt()); preferences.putBool("ledFlashOnUpd", ledFlashOnUpdate->value().toInt());
settingsChanged = true; settingsChanged = true;
} } else {
else
{
preferences.putBool("ledFlashOnUpd", 0); preferences.putBool("ledFlashOnUpd", 0);
settingsChanged = true; settingsChanged = true;
} }
if (request->hasParam("mdnsEnabled", true)) if (request->hasParam("mdnsEnabled", true)) {
{
AsyncWebParameter *mdnsEnabled = request->getParam("mdnsEnabled", true); AsyncWebParameter *mdnsEnabled = request->getParam("mdnsEnabled", true);
preferences.putBool("mdnsEnabled", mdnsEnabled->value().toInt()); preferences.putBool("mdnsEnabled", mdnsEnabled->value().toInt());
settingsChanged = true; settingsChanged = true;
} } else {
else
{
preferences.putBool("mdnsEnabled", 0); preferences.putBool("mdnsEnabled", 0);
settingsChanged = true; settingsChanged = true;
} }
if (request->hasParam("otaEnabled", true)) if (request->hasParam("otaEnabled", true)) {
{
AsyncWebParameter *otaEnabled = request->getParam("otaEnabled", true); AsyncWebParameter *otaEnabled = request->getParam("otaEnabled", true);
preferences.putBool("otaEnabled", otaEnabled->value().toInt()); preferences.putBool("otaEnabled", otaEnabled->value().toInt());
settingsChanged = true; settingsChanged = true;
} } else {
else
{
preferences.putBool("otaEnabled", 0); preferences.putBool("otaEnabled", 0);
settingsChanged = true; settingsChanged = true;
} }
if (request->hasParam("stealFocusOnBlock", true)) if (request->hasParam("stealFocusOnBlock", true)) {
{ AsyncWebParameter *stealFocusOnBlock =
AsyncWebParameter *stealFocusOnBlock = request->getParam("stealFocusOnBlock", true); request->getParam("stealFocusOnBlock", true);
preferences.putBool("stealFocus", stealFocusOnBlock->value().toInt()); preferences.putBool("stealFocus", stealFocusOnBlock->value().toInt());
settingsChanged = true; settingsChanged = true;
} } else {
else
{
preferences.putBool("stealFocus", 0); preferences.putBool("stealFocus", 0);
settingsChanged = true; settingsChanged = true;
} }
if (request->hasParam("mcapBigChar", true)) if (request->hasParam("mcapBigChar", true)) {
{
AsyncWebParameter *mcapBigChar = request->getParam("mcapBigChar", true); AsyncWebParameter *mcapBigChar = request->getParam("mcapBigChar", true);
preferences.putBool("mcapBigChar", mcapBigChar->value().toInt()); preferences.putBool("mcapBigChar", mcapBigChar->value().toInt());
settingsChanged = true; settingsChanged = true;
} } else {
else
{
preferences.putBool("mcapBigChar", 0); preferences.putBool("mcapBigChar", 0);
settingsChanged = true; settingsChanged = true;
} }
if (request->hasParam("mempoolInstance", true)) if (request->hasParam("mempoolInstance", true)) {
{ AsyncWebParameter *mempoolInstance =
AsyncWebParameter *mempoolInstance = request->getParam("mempoolInstance", true); request->getParam("mempoolInstance", true);
preferences.putString("mempoolInstance", mempoolInstance->value().c_str()); preferences.putString("mempoolInstance", mempoolInstance->value().c_str());
settingsChanged = true; settingsChanged = true;
} }
if (request->hasParam("hostnamePrefix", true)) if (request->hasParam("hostnamePrefix", true)) {
{ AsyncWebParameter *hostnamePrefix =
AsyncWebParameter *hostnamePrefix = request->getParam("hostnamePrefix", true); request->getParam("hostnamePrefix", true);
preferences.putString("hostnamePrefix", hostnamePrefix->value().c_str()); preferences.putString("hostnamePrefix", hostnamePrefix->value().c_str());
settingsChanged = true; settingsChanged = true;
} }
if (request->hasParam("ledBrightness", true)) if (request->hasParam("ledBrightness", true)) {
{
AsyncWebParameter *ledBrightness = request->getParam("ledBrightness", true); AsyncWebParameter *ledBrightness = request->getParam("ledBrightness", true);
preferences.putUInt("ledBrightness", ledBrightness->value().toInt()); preferences.putUInt("ledBrightness", ledBrightness->value().toInt());
settingsChanged = true; settingsChanged = true;
} }
if (request->hasParam("fullRefreshMin", true)) if (request->hasParam("fullRefreshMin", true)) {
{ AsyncWebParameter *fullRefreshMin =
AsyncWebParameter *fullRefreshMin = request->getParam("fullRefreshMin", true); request->getParam("fullRefreshMin", true);
preferences.putUInt("fullRefreshMin", fullRefreshMin->value().toInt()); preferences.putUInt("fullRefreshMin", fullRefreshMin->value().toInt());
settingsChanged = true; settingsChanged = true;
} }
if (request->hasParam("wpTimeout", true)) if (request->hasParam("wpTimeout", true)) {
{
AsyncWebParameter *wpTimeout = request->getParam("wpTimeout", true); AsyncWebParameter *wpTimeout = request->getParam("wpTimeout", true);
preferences.putUInt("wpTimeout", wpTimeout->value().toInt()); preferences.putUInt("wpTimeout", wpTimeout->value().toInt());
@ -644,20 +597,17 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
std::vector<std::string> screenNameMap = getScreenNameMap(); std::vector<std::string> screenNameMap = getScreenNameMap();
if (request->hasParam("screens")) if (request->hasParam("screens")) {
{
AsyncWebParameter *screenParam = request->getParam("screens", true); AsyncWebParameter *screenParam = request->getParam("screens", true);
Serial.printf(screenParam->value().c_str()); Serial.printf(screenParam->value().c_str());
} }
for (int i = 0; i < screenNameMap.size(); i++) for (int i = 0; i < screenNameMap.size(); i++) {
{
String key = "screen[" + String(i) + "]"; String key = "screen[" + String(i) + "]";
String prefKey = "screen" + String(i) + "Visible"; String prefKey = "screen" + String(i) + "Visible";
bool visible = false; bool visible = false;
if (request->hasParam(key, true)) if (request->hasParam(key, true)) {
{
AsyncWebParameter *screenParam = request->getParam(key, true); AsyncWebParameter *screenParam = request->getParam(key, true);
visible = screenParam->value().toInt(); visible = screenParam->value().toInt();
} }
@ -665,24 +615,21 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
preferences.putBool(prefKey.c_str(), visible); preferences.putBool(prefKey.c_str(), visible);
} }
if (request->hasParam("tzOffset", true)) if (request->hasParam("tzOffset", true)) {
{
AsyncWebParameter *p = request->getParam("tzOffset", true); AsyncWebParameter *p = request->getParam("tzOffset", true);
int tzOffsetSeconds = p->value().toInt() * 60; int tzOffsetSeconds = p->value().toInt() * 60;
preferences.putInt("gmtOffset", tzOffsetSeconds); preferences.putInt("gmtOffset", tzOffsetSeconds);
settingsChanged = true; settingsChanged = true;
} }
if (request->hasParam("minSecPriceUpd", true)) if (request->hasParam("minSecPriceUpd", true)) {
{
AsyncWebParameter *p = request->getParam("minSecPriceUpd", true); AsyncWebParameter *p = request->getParam("minSecPriceUpd", true);
int minSecPriceUpd = p->value().toInt(); int minSecPriceUpd = p->value().toInt();
preferences.putUInt("minSecPriceUpd", minSecPriceUpd); preferences.putUInt("minSecPriceUpd", minSecPriceUpd);
settingsChanged = true; settingsChanged = true;
} }
if (request->hasParam("timePerScreen", true)) if (request->hasParam("timePerScreen", true)) {
{
AsyncWebParameter *p = request->getParam("timePerScreen", true); AsyncWebParameter *p = request->getParam("timePerScreen", true);
uint timerSeconds = p->value().toInt() * 60; uint timerSeconds = p->value().toInt() * 60;
preferences.putUInt("timerSeconds", timerSeconds); preferences.putUInt("timerSeconds", timerSeconds);
@ -690,15 +637,14 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
} }
request->send(200); request->send(200);
if (settingsChanged) if (settingsChanged) {
{
queueLedEffect(LED_FLASH_SUCCESS); queueLedEffect(LED_FLASH_SUCCESS);
} }
} }
void onApiSystemStatus(AsyncWebServerRequest *request) void onApiSystemStatus(AsyncWebServerRequest *request) {
{ AsyncResponseStream *response =
AsyncResponseStream *response = request->beginResponseStream("application/json"); request->beginResponseStream("application/json");
StaticJsonDocument<128> root; StaticJsonDocument<128> root;
@ -717,16 +663,17 @@ void onApiSystemStatus(AsyncWebServerRequest *request)
#define STRINGIFY(x) #x #define STRINGIFY(x) #x
#define ENUM_TO_STRING(x) STRINGIFY(x) #define ENUM_TO_STRING(x) STRINGIFY(x)
void onApiSetWifiTxPower(AsyncWebServerRequest *request) {
void onApiSetWifiTxPower(AsyncWebServerRequest *request)
{
if (request->hasParam("txPower")) { if (request->hasParam("txPower")) {
AsyncWebParameter *txPowerParam = request->getParam("txPower"); AsyncWebParameter *txPowerParam = request->getParam("txPower");
int txPower = txPowerParam->value().toInt(); int txPower = txPowerParam->value().toInt();
if (static_cast<int>(wifi_power_t::WIFI_POWER_MINUS_1dBm) <= txPower && if (static_cast<int>(wifi_power_t::WIFI_POWER_MINUS_1dBm) <= txPower &&
txPower <= static_cast<int>(wifi_power_t::WIFI_POWER_19_5dBm)) { txPower <= static_cast<int>(wifi_power_t::WIFI_POWER_19_5dBm)) {
// is valid value // is valid value
String txPowerName = std::to_string(static_cast<std::underlying_type_t<wifi_power_t>>(txPower)).c_str(); String txPowerName =
std::to_string(
static_cast<std::underlying_type_t<wifi_power_t>>(txPower))
.c_str();
Serial.printf("Set WiFi Tx power to: %s\n", txPowerName); Serial.printf("Set WiFi Tx power to: %s\n", txPowerName);
@ -741,36 +688,30 @@ void onApiSetWifiTxPower(AsyncWebServerRequest *request)
return request->send(400); return request->send(400);
} }
void onApiLightsStatus(AsyncWebServerRequest *request) {
void onApiLightsStatus(AsyncWebServerRequest *request) AsyncResponseStream *response =
{ request->beginResponseStream("application/json");
AsyncResponseStream *response = request->beginResponseStream("application/json");
serializeJson(getLedStatusObject()["data"], *response); serializeJson(getLedStatusObject()["data"], *response);
request->send(response); request->send(response);
} }
void onApiLightsOff(AsyncWebServerRequest *request) void onApiLightsOff(AsyncWebServerRequest *request) {
{
setLights(0, 0, 0); setLights(0, 0, 0);
request->send(200); request->send(200);
} }
void onApiLightsSetColor(AsyncWebServerRequest *request) void onApiLightsSetColor(AsyncWebServerRequest *request) {
{ if (request->hasParam("c")) {
if (request->hasParam("c")) AsyncResponseStream *response =
{ request->beginResponseStream("application/json");
AsyncResponseStream *response = request->beginResponseStream("application/json");
String rgbColor = request->getParam("c")->value(); String rgbColor = request->getParam("c")->value();
if (rgbColor.compareTo("off") == 0) if (rgbColor.compareTo("off") == 0) {
{
setLights(0, 0, 0); setLights(0, 0, 0);
} } else {
else
{
uint 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);
@ -779,7 +720,6 @@ void onApiLightsSetColor(AsyncWebServerRequest *request)
StaticJsonDocument<48> doc; StaticJsonDocument<48> doc;
doc["result"] = rgbColor; doc["result"] = rgbColor;
serializeJson(getLedStatusObject()["data"], *response); serializeJson(getLedStatusObject()["data"], *response);
request->send(response); request->send(response);
@ -788,48 +728,39 @@ void onApiLightsSetColor(AsyncWebServerRequest *request)
} }
} }
void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json) void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json) {
{
JsonArray lights = json.as<JsonArray>(); JsonArray lights = json.as<JsonArray>();
if (lights.size() != pixels.numPixels()) if (lights.size() != pixels.numPixels()) {
{
Serial.printf("Invalid values for LED set %d\n", lights.size()); Serial.printf("Invalid values for LED set %d\n", lights.size());
request->send(400); request->send(400);
return; return;
} }
for (uint i = 0; i < pixels.numPixels(); i++) for (uint i = 0; i < pixels.numPixels(); i++) {
{
unsigned int red, green, blue; unsigned int red, green, blue;
if (lights[i].containsKey("red") && lights[i].containsKey("green") && lights[i].containsKey("blue")) if (lights[i].containsKey("red") && lights[i].containsKey("green") &&
{ lights[i].containsKey("blue")) {
red = lights[i]["red"].as<uint>(); red = lights[i]["red"].as<uint>();
green = lights[i]["green"].as<uint>(); green = lights[i]["green"].as<uint>();
blue = lights[i]["blue"].as<uint>(); blue = lights[i]["blue"].as<uint>();
} } else if (lights[i].containsKey("hex")) {
else if (lights[i].containsKey("hex")) if (!sscanf(lights[i]["hex"].as<String>().c_str(), "#%02X%02X%02X", &red,
{ &green, &blue) == 3) {
if (!sscanf(lights[i]["hex"].as<String>().c_str(), "#%02X%02X%02X", &red, &green, &blue) == 3)
{
Serial.printf("Invalid hex for LED %d\n", i); Serial.printf("Invalid hex for LED %d\n", i);
request->send(400); request->send(400);
return; return;
} }
} } else {
else
{
Serial.printf("No valid color for LED %d\n", i); Serial.printf("No valid color for LED %d\n", i);
request->send(400); request->send(400);
return; return;
} }
pixels.setPixelColor((pixels.numPixels() - i - 1), pixels.Color( pixels.setPixelColor((pixels.numPixels() - i - 1),
red, pixels.Color(red, green, blue));
green,
blue));
} }
pixels.show(); pixels.show();
@ -838,10 +769,11 @@ void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json)
request->send(200); request->send(200);
} }
void onIndex(AsyncWebServerRequest *request) { request->send(LittleFS, "/index.html", String(), false); } void onIndex(AsyncWebServerRequest *request) {
request->send(LittleFS, "/index.html", String(), false);
}
void onNotFound(AsyncWebServerRequest *request) void onNotFound(AsyncWebServerRequest *request) {
{
// Serial.printf("NotFound, URL[%s]\n", request->url()); // Serial.printf("NotFound, URL[%s]\n", request->url());
// Serial.printf("NotFound, METHOD[%s]\n", request->methodToString()); // Serial.printf("NotFound, METHOD[%s]\n", request->methodToString());
@ -851,7 +783,8 @@ void onNotFound(AsyncWebServerRequest *request)
// for (i = 0; i < headers; i++) // for (i = 0; i < headers; i++)
// { // {
// AsyncWebHeader *h = request->getHeader(i); // AsyncWebHeader *h = request->getHeader(i);
// Serial.printf("NotFound HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); // Serial.printf("NotFound HEADER[%s]: %s\n", h->name().c_str(),
// h->value().c_str());
// } // }
// int params = request->params(); // int params = request->params();
@ -860,37 +793,36 @@ void onNotFound(AsyncWebServerRequest *request)
// AsyncWebParameter *p = request->getParam(i); // AsyncWebParameter *p = request->getParam(i);
// if (p->isFile()) // if (p->isFile())
// { // p->isPost() is also true // { // p->isPost() is also true
// Serial.printf("NotFound FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); // Serial.printf("NotFound FILE[%s]: %s, size: %u\n",
// p->name().c_str(), p->value().c_str(), p->size());
// } // }
// else if (p->isPost()) // else if (p->isPost())
// { // {
// Serial.printf("NotFound POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); // Serial.printf("NotFound POST[%s]: %s\n", p->name().c_str(),
// p->value().c_str());
// } // }
// else // else
// { // {
// Serial.printf("NotFound GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); // Serial.printf("NotFound GET[%s]: %s\n", p->name().c_str(),
// p->value().c_str());
// } // }
// } // }
// Access-Control-Request-Method == POST might be better // Access-Control-Request-Method == POST might be better
if (request->method() == HTTP_OPTIONS || request->hasHeader("Sec-Fetch-Mode")) if (request->method() == HTTP_OPTIONS ||
{ request->hasHeader("Sec-Fetch-Mode")) {
// Serial.printf("NotFound, Return[%d]\n", 200); // Serial.printf("NotFound, Return[%d]\n", 200);
request->send(200); request->send(200);
} } else {
else
{
// Serial.printf("NotFound, Return[%d]\n", 404); // Serial.printf("NotFound, Return[%d]\n", 404);
request->send(404); request->send(404);
} }
}; };
void eventSourceTask(void *pvParameters) void eventSourceTask(void *pvParameters) {
{ for (;;) {
for (;;)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
eventSourceUpdate(); eventSourceUpdate();
} }

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "WebServer.h" #include "WebServer.h"
#include "ESPAsyncWebServer.h" #include "ESPAsyncWebServer.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
@ -7,10 +8,19 @@
#include "AsyncJson.h" #include "AsyncJson.h"
#include <iostream> #include <iostream>
// #include "AsyncJson.h"
// #include "ESPAsyncWebServer.h"
// #include "WebServer.h"
// #include <ArduinoJson.h>
// #include <ESPmDNS.h>
// #include <LittleFS.h>
// #include <iostream>
#include "lib/block_notify.hpp" #include "lib/block_notify.hpp"
#include "lib/led_handler.hpp"
#include "lib/price_notify.hpp" #include "lib/price_notify.hpp"
#include "lib/screen_handler.hpp" #include "lib/screen_handler.hpp"
#include "lib/led_handler.hpp"
#include "webserver/OneParamRewrite.hpp" #include "webserver/OneParamRewrite.hpp"
@ -40,7 +50,6 @@ void onApiLightsOff(AsyncWebServerRequest *request);
void onApiLightsSetColor(AsyncWebServerRequest *request); void onApiLightsSetColor(AsyncWebServerRequest *request);
void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json); void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json);
void onApiRestart(AsyncWebServerRequest *request); void onApiRestart(AsyncWebServerRequest *request);
void onIndex(AsyncWebServerRequest *request); void onIndex(AsyncWebServerRequest *request);

View File

@ -1,43 +1,31 @@
#include "OneParamRewrite.hpp" #include "OneParamRewrite.hpp"
OneParamRewrite::OneParamRewrite(const char *from, const char *to) OneParamRewrite::OneParamRewrite(const char *from, const char *to)
: AsyncWebRewrite(from, to) : AsyncWebRewrite(from, to) {
{
_paramIndex = _from.indexOf('{'); _paramIndex = _from.indexOf('{');
if (_paramIndex >= 0 && _from.endsWith("}")) if (_paramIndex >= 0 && _from.endsWith("}")) {
{
_urlPrefix = _from.substring(0, _paramIndex); _urlPrefix = _from.substring(0, _paramIndex);
int index = _params.indexOf('{'); int index = _params.indexOf('{');
if (index >= 0) if (index >= 0) {
{
_params = _params.substring(0, index); _params = _params.substring(0, index);
} }
} } else {
else
{
_urlPrefix = _from; _urlPrefix = _from;
} }
_paramsBackup = _params; _paramsBackup = _params;
} }
bool OneParamRewrite::match(AsyncWebServerRequest *request) bool OneParamRewrite::match(AsyncWebServerRequest *request) {
{ if (request->url().startsWith(_urlPrefix)) {
if (request->url().startsWith(_urlPrefix)) if (_paramIndex >= 0) {
{
if (_paramIndex >= 0)
{
_params = _paramsBackup + request->url().substring(_paramIndex); _params = _paramsBackup + request->url().substring(_paramIndex);
} } else {
else
{
_params = _paramsBackup; _params = _paramsBackup;
} }
return true; return true;
} } else {
else
{
return false; return false;
} }
}; };

View File

@ -2,8 +2,7 @@
#include "ESPAsyncWebServer.h" #include "ESPAsyncWebServer.h"
class OneParamRewrite : public AsyncWebRewrite class OneParamRewrite : public AsyncWebRewrite {
{
protected: protected:
String _urlPrefix; String _urlPrefix;
int _paramIndex; int _paramIndex;

View File

@ -1,23 +1,33 @@
/*
* Copyright 2023 Djuri Baars
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Arduino.h" #include "Arduino.h"
#include <WiFiManager.h> #include <WiFiManager.h>
#define WEBSERVER_H #define WEBSERVER_H
#include "ESPAsyncWebServer.h" #include "ESPAsyncWebServer.h"
#include "lib/config.hpp" #include "lib/config.hpp"
//char ptrTaskList[400];
uint wifiLostConnection; uint wifiLostConnection;
extern "C" void app_main() extern "C" void app_main() {
{
initArduino(); initArduino();
Serial.begin(115200); Serial.begin(115200);
setup(); setup();
while (true) while (true) {
{
// vTaskList(ptrTaskList); // vTaskList(ptrTaskList);
// Serial.println(F("**********************************")); // Serial.println(F("**********************************"));
// Serial.println(F("Task State Prio Stack Num")); // Serial.println(F("Task State Prio Stack Num"));