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 = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x66, 0x66, 0x66,
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 = {
(uint8_t *)Antonio_SemiBold20pt7bBitmaps,
(GFXglyph *)Antonio_SemiBold20pt7bGlyphs,
0x20, 0x7E, 51 };
(GFXglyph *)Antonio_SemiBold20pt7bGlyphs, 0x20, 0x7E, 51};
// Approx. 5193 bytes

View file

@ -1,3 +1,6 @@
#include <Adafruit_GFX.h>
#include <Arduino.h>
const uint8_t Antonio_SemiBold30pt7bBitmaps[] PROGMEM = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xE7,
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 = {
(uint8_t *)Antonio_SemiBold30pt7bBitmaps,
(GFXglyph *)Antonio_SemiBold30pt7bGlyphs,
0x20, 0x7E, 76 };
(GFXglyph *)Antonio_SemiBold30pt7bGlyphs, 0x20, 0x7E, 76};
// Approx. 10860 bytes

View file

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

View file

@ -1,3 +1,6 @@
#include <Adafruit_GFX.h>
#include <Arduino.h>
const uint8_t Antonio_SemiBold90pt7bBitmaps[] PROGMEM = {
0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF,
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'
{55437, 64, 151, 72, 4, 106}, // 0x58 'X'
{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 = {
(uint8_t *)Antonio_SemiBold90pt7bBitmaps,
(GFXglyph *)Antonio_SemiBold90pt7bGlyphs,
0x20, 0x5B, 228 };
(GFXglyph *)Antonio_SemiBold90pt7bGlyphs, 0x20, 0x5B, 228};
// Approx. 60745 bytes

View file

@ -48,21 +48,19 @@ uint currentBlockHeight = 816000;
// ew==
// -----END CERTIFICATE-----)";
void setupBlockNotify()
{
void setupBlockNotify() {
// currentBlockHeight = preferences.getUInt("blockHeight", 816000);
IPAddress result;
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);
if (dnsErr != 1)
{
if (dnsErr != 1) {
Serial.print(mempoolInstance);
Serial.println(F("mempool DNS could not be resolved"));
WiFi.reconnect();
@ -75,19 +73,18 @@ void setupBlockNotify()
http->begin("https://" + mempoolInstance + "/api/blocks/tip/height");
int httpCode = http->GET();
if (httpCode > 0 && httpCode == HTTP_CODE_OK)
{
if (httpCode > 0 && httpCode == HTTP_CODE_OK) {
String blockHeightStr = http->getString();
currentBlockHeight = blockHeightStr.toInt();
// xTaskNotifyGive(blockUpdateTaskHandle);
if (workQueue != nullptr)
{
if (workQueue != nullptr) {
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
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 = {
.uri = "wss://mempool.space/api/v1/ws",
@ -97,22 +94,22 @@ void setupBlockNotify()
};
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);
}
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;
const String sub = "{\"action\": \"want\", \"data\":[\"blocks\"]}";
switch (event_id)
{
switch (event_id) {
case WEBSOCKET_EVENT_CONNECTED:
Serial.println(F("Connected to Mempool.space WebSocket"));
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"));
}
@ -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);
deserializeJson(doc, (char *)event_data->data_ptr);
if (doc.containsKey("block"))
{
if (doc.containsKey("block")) {
JsonObject block = doc["block"];
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>());
preferences.putUInt("blockHeight", currentBlockHeight);
if (workQueue != nullptr)
{
if (workQueue != nullptr) {
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
// xTaskNotifyGive(blockUpdateTaskHandle);
if (getCurrentScreen() != SCREEN_BLOCK_HEIGHT && preferences.getBool("stealFocus", true))
{
if (getCurrentScreen() != SCREEN_BLOCK_HEIGHT &&
preferences.getBool("stealFocus", true)) {
uint64_t timerPeriod = 0;
if (isTimerActive()) {
// 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);
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
queueLedEffect(LED_FLASH_BLOCK_NOTIFY);
}
@ -175,25 +170,19 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data)
doc.clear();
}
uint getBlockHeight()
{
return currentBlockHeight;
}
uint getBlockHeight() { return currentBlockHeight; }
void setBlockHeight(uint newBlockHeight)
{
void setBlockHeight(uint newBlockHeight) {
currentBlockHeight = newBlockHeight;
}
bool isBlockNotifyConnected()
{
bool isBlockNotifyConnected() {
if (blockNotifyClient == NULL)
return false;
return esp_websocket_client_is_connected(blockNotifyClient);
}
void stopBlockNotify()
{
void stopBlockNotify() {
esp_websocket_client_stop(blockNotifyClient);
esp_websocket_client_destroy(blockNotifyClient);
}

View file

@ -1,21 +1,22 @@
#pragma once
#include <cstring>
#include <string>
#include "lib/led_handler.hpp"
#include "lib/screen_handler.hpp"
#include "lib/shared.hpp"
#include <Arduino.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "shared.hpp"
#include "esp_timer.h"
#include "esp_websocket_client.h"
#include "screen_handler.hpp"
#include "led_handler.hpp"
#include <HTTPClient.h>
#include <cstring>
#include <esp_timer.h>
#include <esp_websocket_client.h>
#include <string>
// using namespace websockets;
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 setBlockHeight(uint newBlockHeight);

View file

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

View file

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

View file

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

View file

@ -1,26 +1,23 @@
#pragma once;
#include <WiFiManager.h>
#include "base64.h"
#include <WiFiClientSecure.h>
#include <Preferences.h>
#include <Adafruit_MCP23X17.h>
#include <Arduino.h>
#include "shared.hpp"
#include "epd.hpp"
#include "improv.hpp"
#include "esp_task_wdt.h"
#include <Preferences.h>
#include <WiFiClientSecure.h>
#include <WiFiManager.h>
#include <base64.h>
#include <esp_task_wdt.h>
#include <map>
#include "ota.hpp"
#include "lib/screen_handler.hpp"
#include "lib/webserver.hpp"
#include "lib/block_notify.hpp"
#include "lib/price_notify.hpp"
#include "lib/button_handler.hpp"
#include "lib/epd.hpp"
#include "lib/improv.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 DEFAULT_MEMPOOL_INSTANCE "mempool.space"

View file

@ -16,21 +16,12 @@ Native_Pin EPD_CS[NUM_SCREENS] = {
#endif
};
Native_Pin EPD_BUSY[NUM_SCREENS] = {
Native_Pin(3),
Native_Pin(5),
Native_Pin(7),
Native_Pin(9),
Native_Pin(37),
Native_Pin(18),
Native_Pin(16),
Native_Pin(3), Native_Pin(5), Native_Pin(7), Native_Pin(9),
Native_Pin(37), Native_Pin(18), Native_Pin(16),
};
MCP23X17_Pin EPD_RESET_MPD[NUM_SCREENS] = {
MCP23X17_Pin(mcp1, 8),
MCP23X17_Pin(mcp1, 9),
MCP23X17_Pin(mcp1, 10),
MCP23X17_Pin(mcp1, 11),
MCP23X17_Pin(mcp1, 12),
MCP23X17_Pin(mcp1, 13),
MCP23X17_Pin(mcp1, 8), MCP23X17_Pin(mcp1, 9), MCP23X17_Pin(mcp1, 10),
MCP23X17_Pin(mcp1, 11), MCP23X17_Pin(mcp1, 12), MCP23X17_Pin(mcp1, 13),
MCP23X17_Pin(mcp1, 14),
};
@ -39,36 +30,20 @@ Native_Pin EPD_DC = Native_Pin(14);
Native_Pin EPD_DC = Native_Pin(38);
MCP23X17_Pin EPD_BUSY[NUM_SCREENS] = {
MCP23X17_Pin(mcp1, 8),
MCP23X17_Pin(mcp1, 9),
MCP23X17_Pin(mcp1, 10),
MCP23X17_Pin(mcp1, 11),
MCP23X17_Pin(mcp1, 12),
MCP23X17_Pin(mcp1, 13),
MCP23X17_Pin(mcp1, 14),
MCP23X17_Pin(mcp1, 4),
MCP23X17_Pin(mcp1, 8), MCP23X17_Pin(mcp1, 9), MCP23X17_Pin(mcp1, 10),
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(mcp2, 8),
MCP23X17_Pin(mcp2, 10),
MCP23X17_Pin(mcp2, 12),
MCP23X17_Pin(mcp2, 14),
MCP23X17_Pin(mcp2, 0),
MCP23X17_Pin(mcp2, 2),
MCP23X17_Pin(mcp2, 4),
MCP23X17_Pin(mcp2, 6)
};
MCP23X17_Pin(mcp2, 8), MCP23X17_Pin(mcp2, 10), MCP23X17_Pin(mcp2, 12),
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(mcp2, 9),
MCP23X17_Pin(mcp2, 11),
MCP23X17_Pin(mcp2, 13),
MCP23X17_Pin(mcp2, 15),
MCP23X17_Pin(mcp2, 1),
MCP23X17_Pin(mcp2, 3),
MCP23X17_Pin(mcp2, 5),
MCP23X17_Pin(mcp2, 7),
MCP23X17_Pin(mcp2, 9), MCP23X17_Pin(mcp2, 11), MCP23X17_Pin(mcp2, 13),
MCP23X17_Pin(mcp2, 15), MCP23X17_Pin(mcp2, 1), MCP23X17_Pin(mcp2, 3),
MCP23X17_Pin(mcp2, 5), MCP23X17_Pin(mcp2, 7),
};
#endif
@ -108,37 +83,32 @@ std::mutex epdMutex[NUM_SCREENS];
uint8_t qrcode[800];
void forceFullRefresh()
{
for (uint i = 0; i < NUM_SCREENS; i++)
{
void forceFullRefresh() {
for (uint i = 0; i < NUM_SCREENS; i++) {
lastFullRefresh[i] = NULL;
}
}
void refreshFromMemory()
{
for (uint i = 0; i < NUM_SCREENS; i++)
{
void refreshFromMemory() {
for (uint i = 0; i < NUM_SCREENS; i++) {
int *taskParam = new int;
*taskParam = i;
xTaskCreate([](void *pvParameters)
{
xTaskCreate(
[](void *pvParameters) {
const int epdIndex = *(int *)pvParameters;
delete (int *)pvParameters;
displays[epdIndex].refresh(false);
vTaskDelete(NULL); },
vTaskDelete(NULL);
},
"PrepareUpd", 4096, taskParam, tskIDLE_PRIORITY, NULL);
}
}
void setupDisplays()
{
void setupDisplays() {
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);
}
@ -146,30 +116,23 @@ void setupDisplays()
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();
// xSemaphoreGive(epdUpdateSemaphore[i]);
int *taskParam = new int;
*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",
"T",
"C",
"L",
"O",
"C",
"K"};
epdContent = {"B", "T", "C", "L", "O", "C", "K"};
setEpdContent(epdContent);
}
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent)
{
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent) {
setEpdContent(newEpdContent, false);
}
@ -183,16 +146,14 @@ void setEpdContent(std::array<std::string, NUM_SCREENS> newEpdContent) {
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);
waitUntilNoneBusy();
for (uint i = 0; i < NUM_SCREENS; i++)
{
if (newEpdContent[i].compareTo(currentEpdContent[i]) != 0 || forceUpdate)
{
for (uint i = 0; i < NUM_SCREENS; i++) {
if (newEpdContent[i].compareTo(currentEpdContent[i]) != 0 || forceUpdate) {
epdContent[i] = newEpdContent[i];
UpdateDisplayTaskItem dispUpdate = {i};
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;
while (1)
{
while (1) {
// 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;
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;
if (strstr(epdContent[epdIndex].c_str(), "/") != NULL)
{
String top = epdContent[epdIndex].substring(0, epdContent[epdIndex].indexOf("/"));
String bottom = epdContent[epdIndex].substring(epdContent[epdIndex].indexOf("/") + 1);
if (strstr(epdContent[epdIndex].c_str(), "/") != NULL) {
String top = epdContent[epdIndex].substring(
0, epdContent[epdIndex].indexOf("/"));
String bottom = epdContent[epdIndex].substring(
epdContent[epdIndex].indexOf("/") + 1);
splitText(epdIndex, top, bottom, updatePartial);
}
else if (epdContent[epdIndex].startsWith(F("qr")))
{
} else if (epdContent[epdIndex].startsWith(F("qr"))) {
renderQr(epdIndex, epdContent[epdIndex], updatePartial);
}
else if (epdContent[epdIndex].length() > 5)
{
} else if (epdContent[epdIndex].length() > 5) {
renderText(epdIndex, epdContent[epdIndex], updatePartial);
}
else
{
} else {
if (epdContent[epdIndex].length() > 1)
{
showChars(epdIndex, epdContent[epdIndex], updatePartial, &FONT_MEDIUM);
}
else
{
showDigit(epdIndex, epdContent[epdIndex].c_str()[0], updatePartial, &FONT_BIG);
if (epdContent[epdIndex].length() > 1) {
showChars(epdIndex, epdContent[epdIndex], updatePartial,
&FONT_MEDIUM);
} else {
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;
delete (int *)pvParameters;
for (;;)
{
for (;;) {
// Wait for the task notification
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
@ -265,8 +216,7 @@ extern "C" void updateDisplay(void *pvParameters) noexcept
displays[epdIndex].init(0, false, 40);
}
uint count = 0;
while (EPD_BUSY[epdIndex].digitalRead() == HIGH || count < 10)
{
while (EPD_BUSY[epdIndex].digitalRead() == HIGH || count < 10) {
vTaskDelay(pdMS_TO_TICKS(100));
count++;
}
@ -274,16 +224,17 @@ extern "C" void updateDisplay(void *pvParameters) noexcept
bool updatePartial = true;
// 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;
}
char tries = 0;
while (tries < 3)
{
if (displays[epdIndex].displayWithReturn(updatePartial))
{
while (tries < 3) {
if (displays[epdIndex].displayWithReturn(updatePartial)) {
displays[epdIndex].powerOff();
currentEpdContent[epdIndex] = epdContent[epdIndex];
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].setFont(&FONT_SMALL);
displays[dispNum].setTextColor(getFgColor());
@ -312,14 +263,16 @@ void splitText(const uint dispNum, const String &top, const String &bottom, bool
uint16_t ttbw, ttbh;
displays[dispNum].getTextBounds(top, 0, 0, &ttbx, &ttby, &ttbw, &ttbh);
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
int16_t tbbx, tbby;
uint16_t tbbw, tbbh;
displays[dispNum].getTextBounds(bottom, 0, 0, &tbbx, &tbby, &tbbw, &tbbh);
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.
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].setCursor(tx, ty);
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].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);
displays[dispNum].setRotation(2);
displays[dispNum].setFont(font);
@ -354,8 +308,8 @@ void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font)
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].setFont(font);
displays[dispNum].setTextColor(getFgColor());
@ -370,34 +324,21 @@ void showChars(const uint dispNum, const String &chars, bool partial, const GFXf
displays[dispNum].print(chars);
}
int getBgColor()
{
return bgColor;
}
int getBgColor() { return bgColor; }
int getFgColor()
{
return fgColor;
}
int getFgColor() { return fgColor; }
void setBgColor(int color)
{
bgColor = color;
}
void setBgColor(int color) { bgColor = color; }
void setFgColor(int color)
{
fgColor = color;
}
void setFgColor(int color) { fgColor = color; }
std::array<String, NUM_SCREENS> getCurrentEpdContent()
{
std::array<String, NUM_SCREENS> getCurrentEpdContent() {
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].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].setTextColor(GxEPD_BLACK);
displays[dispNum].setCursor(0, 50);
@ -407,67 +348,61 @@ void renderText(const uint dispNum, const String &text, bool partial)
std::string line;
while (std::getline(ss, line, '\n'))
{
if (line.rfind("*", 0) == 0)
{
while (std::getline(ss, line, '\n')) {
if (line.rfind("*", 0) == 0) {
line.erase(std::remove(line.begin(), line.end(), '*'), line.end());
displays[dispNum].setFont(&FreeSansBold9pt7b);
displays[dispNum].println(line.c_str());
}
else
{
} else {
displays[dispNum].setFont(&FreeSans9pt7b);
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
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);
const int size = qrcodegen_getSize(qrcode);
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].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);
const int border = 0;
for (int y = -border; y < size * 4 + border; y++)
{
for (int x = -border; x < size * 4 + border; x++)
{
displays[dispNum].drawPixel(padding + x, paddingY + y, qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4)) ? GxEPD_BLACK : GxEPD_WHITE);
for (int y = -border; y < size * 4 + border; y++) {
for (int x = -border; x < size * 4 + border; x++) {
displays[dispNum].drawPixel(
padding + x, paddingY + y,
qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4))
? GxEPD_BLACK
: GxEPD_WHITE);
}
}
#endif
}
void waitUntilNoneBusy()
{
for (int i = 0; i < NUM_SCREENS; i++)
{
void waitUntilNoneBusy() {
for (int i = 0; i < NUM_SCREENS; i++) {
uint count = 0;
while (EPD_BUSY[i].digitalRead())
{
while (EPD_BUSY[i].digitalRead()) {
count++;
vTaskDelay(10);
if (count == 200)
{
if (count == 200) {
// displays[i].init(0, false);
vTaskDelay(100);
}
else if (count > 205)
{
} else if (count > 205) {
Serial.printf("Busy timeout %d", i);
break;
}

View file

@ -1,23 +1,22 @@
#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/FreeSansBold9pt7b.h>
#include "lib/config.hpp"
#include "lib/shared.hpp"
#include <Fonts/FreeSans9pt7b.h>
#include <regex>
#include <Fonts/FreeSansBold9pt7b.h>
#include <GxEPD2_BW.h>
#include <mcp23x17_pin.hpp>
#include <mutex>
#include <native_pin.hpp>
#include <regex>
#ifdef USE_QR
#include "qrcodegen.h"
#endif
// extern TaskHandle_t epdTaskHandle;
typedef struct
{
typedef struct {
char dispNum;
} UpdateDisplayTaskItem;
@ -25,10 +24,12 @@ void forceFullRefresh();
void refreshFromMemory();
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 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;
void updateDisplayAlt(int epdIndex);
@ -42,7 +43,8 @@ void setFgColor(int color);
void renderText(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<std::string, NUM_SCREENS> newEpdContent);

View file

@ -2,11 +2,13 @@
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);
}
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;
Command command = (Command)data[0];
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;
}
bool parse_improv_serial_byte(size_t position, uint8_t byte, const uint8_t *buffer,
std::function<bool(ImprovCommand)> &&callback, std::function<void(Error)> &&on_error) {
bool parse_improv_serial_byte(size_t position, uint8_t byte,
const uint8_t *buffer,
std::function<bool(ImprovCommand)> &&callback,
std::function<void(Error)> &&on_error) {
if (position == 0)
return byte == 'I';
if (position == 1)
@ -94,7 +98,9 @@ bool parse_improv_serial_byte(size_t position, uint8_t byte, const uint8_t *buff
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;
uint32_t length = 0;
out.push_back(command);
@ -118,7 +124,9 @@ std::vector<uint8_t> build_rpc_response(Command command, const std::vector<std::
}
#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;
uint32_t length = 0;
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 STATUS_UUID = "00467768-6228-2272-4663-277478268001";
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_RESULT_UUID = "00467768-6228-2272-4663-277478268004";
static const char *const CAPABILITIES_UUID = "00467768-6228-2272-4663-277478268005";
static const char *const RPC_COMMAND_UUID =
"00467768-6228-2272-4663-277478268003";
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 {
ERROR_NONE = 0x00,
@ -61,16 +64,23 @@ struct ImprovCommand {
std::string password;
};
ImprovCommand parse_improv_data(const std::vector<uint8_t> &data, bool check_checksum = true);
ImprovCommand parse_improv_data(const uint8_t *data, size_t length, bool check_checksum = true);
ImprovCommand parse_improv_data(const std::vector<uint8_t> &data,
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,
std::function<bool(ImprovCommand)> &&callback, std::function<void(Error)> &&on_error);
bool parse_improv_serial_byte(size_t position, uint8_t byte,
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);
#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
} // namespace improv

View file

@ -5,30 +5,26 @@ QueueHandle_t ledTaskQueue = NULL;
Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
uint ledTaskParams;
void ledTask(void *parameter)
{
while (1)
{
if (ledTaskQueue != NULL)
{
if (xQueueReceive(ledTaskQueue, &ledTaskParams, portMAX_DELAY) == pdPASS)
{
void ledTask(void *parameter) {
while (1) {
if (ledTaskQueue != NULL) {
if (xQueueReceive(ledTaskQueue, &ledTaskParams, portMAX_DELAY) ==
pdPASS) {
uint32_t oldLights[NEOPIXEL_COUNT];
// get current state
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
oldLights[i] = pixels.getPixelColor(i);
}
switch (ledTaskParams)
{
switch (ledTaskParams) {
case LED_POWER_TEST:
ledRainbow(20);
pixels.clear();
break;
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;
case LED_FLASH_ERROR:
blinkDelayColor(250, 3, 255, 0, 0);
@ -53,25 +49,22 @@ void ledTask(void *parameter)
case LED_FLASH_UPDATE:
break;
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;
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;
case LED_EFFECT_WIFI_ERASE_SETTINGS:
blinkDelay(100, 3);
break;
case LED_EFFECT_WIFI_CONNECTING:
for (int i = NEOPIXEL_COUNT; i >= 0; i--)
{
for (int j = NEOPIXEL_COUNT; j >= 0; j--)
{
if (j == i)
{
for (int i = NEOPIXEL_COUNT; i >= 0; i--) {
for (int j = NEOPIXEL_COUNT; j >= 0; j--) {
if (j == i) {
pixels.setPixelColor(i, pixels.Color(16, 197, 236));
}
else
{
} else {
pixels.setPixelColor(j, pixels.Color(0, 0, 0));
}
}
@ -80,10 +73,8 @@ void ledTask(void *parameter)
}
break;
case LED_EFFECT_PAUSE_TIMER:
for (int i = NEOPIXEL_COUNT; i >= 0; i--)
{
for (int j = NEOPIXEL_COUNT; j >= 0; j--)
{
for (int i = NEOPIXEL_COUNT; i >= 0; i--) {
for (int j = NEOPIXEL_COUNT; j >= 0; j--) {
uint32_t c = pixels.Color(0, 0, 0);
if (i == j)
c = pixels.Color(0, 255, 0);
@ -109,11 +100,9 @@ void ledTask(void *parameter)
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);
if (i == j)
c = pixels.Color(0, 255, 0);
@ -133,8 +122,7 @@ void ledTask(void *parameter)
// 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]);
}
@ -144,17 +132,14 @@ void ledTask(void *parameter)
}
}
void setupLeds()
{
void setupLeds() {
pixels.begin();
pixels.setBrightness(preferences.getUInt("ledBrightness", 128));
pixels.clear();
pixels.show();
setupLedTask();
if (preferences.getBool("ledTestOnPower", true))
{
while (!ledTaskQueue)
{
if (preferences.getBool("ledTestOnPower", true)) {
while (!ledTaskQueue) {
delay(1);
// wait until queue is available
}
@ -162,17 +147,14 @@ void setupLeds()
}
}
void setupLedTask()
{
void setupLedTask() {
ledTaskQueue = xQueueCreate(5, sizeof(uint));
xTaskCreate(ledTask, "LedTask", 2048, NULL, tskIDLE_PRIORITY, &ledTaskHandle);
}
void blinkDelay(int d, int times)
{
for (int j = 0; j < times; j++)
{
void blinkDelay(int d, int times) {
for (int j = 0; j < times; j++) {
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
pixels.setPixelColor(1, pixels.Color(0, 255, 0));
@ -192,12 +174,9 @@ void blinkDelay(int d, int times)
pixels.show();
}
void blinkDelayColor(int d, int times, uint r, uint g, uint b)
{
for (int j = 0; j < times; j++)
{
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
void blinkDelayColor(int d, int times, uint r, uint g, uint b) {
for (int j = 0; j < times; j++) {
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
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();
}
void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2)
{
for (int j = 0; j < times; j++)
{
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2) {
for (int j = 0; j < times; j++) {
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
pixels.setPixelColor(i, c1);
}
pixels.show();
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.show();
@ -234,43 +209,33 @@ void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2)
pixels.show();
}
void clearLeds()
{
void clearLeds() {
preferences.putBool("ledStatus", false);
pixels.clear();
pixels.show();
}
void setLights(int r, int g, int b)
{
setLights(pixels.Color(r, g, b));
}
void setLights(int r, int g, int b) { setLights(pixels.Color(r, g, b)); }
void setLights(uint32_t color)
{
void setLights(uint32_t color) {
bool ledStatus = true;
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
pixels.setPixelColor(i, color);
}
pixels.show();
if (color == pixels.Color(0, 0, 0))
{
if (color == pixels.Color(0, 0, 0)) {
ledStatus = false;
} else {
saveLedState();
}
preferences.putBool("ledStatus", ledStatus);
}
void saveLedState() {
for (int i = 0; i < pixels.numPixels(); i++)
{
for (int i = 0; i < pixels.numPixels(); i++) {
int pixelColor = pixels.getPixelColor(i);
char key[12];
snprintf(key, 12, "%s%d", "ledColor_", i);
@ -281,8 +246,7 @@ void saveLedState() {
}
void restoreLedState() {
for (int i = 0; i < pixels.numPixels(); i++)
{
for (int i = 0; i < pixels.numPixels(); i++) {
char key[12];
snprintf(key, 12, "%s%d", "ledColor_", i);
uint pixelColor = preferences.getUInt(key, pixels.Color(0, 0, 0));
@ -292,15 +256,10 @@ void restoreLedState() {
pixels.show();
}
QueueHandle_t getLedTaskQueue()
{
return ledTaskQueue;
}
QueueHandle_t getLedTaskQueue() { return ledTaskQueue; }
bool queueLedEffect(uint effect)
{
if (ledTaskQueue == NULL)
{
bool queueLedEffect(uint effect) {
if (ledTaskQueue == NULL) {
return false;
}
@ -308,14 +267,13 @@ bool queueLedEffect(uint effect)
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.
// 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
// 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
// optionally a few extras: number of rainbow repetitions (default 1),
// 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)
{
for (int a = 0; a < 10; a++)
{ // Repeat 10 times...
for (int b = 0; b < 3; b++)
{ // 'b' counts from 0 to 2...
void ledTheaterChase(uint32_t color, int wait) {
for (int a = 0; a < 10; a++) { // 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)
// '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.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)
for (int a = 0; a < 30; a++)
{ // Repeat 30 times...
for (int b = 0; b < 3; b++)
{ // 'b' counts from 0 to 2...
for (int a = 0; a < 30; a++) { // Repeat 30 times...
for (int b = 0; b < 3; b++) { // 'b' counts from 0 to 2...
pixels.clear(); // Set all pixels in RAM to 0 (off)
// '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
// revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps):
@ -373,7 +323,4 @@ void ledTheaterChaseRainbow(int wait)
}
}
Adafruit_NeoPixel getPixels()
{
return pixels;
}
Adafruit_NeoPixel getPixels() { return pixels; }

View file

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

View file

@ -2,10 +2,8 @@
TaskHandle_t taskOtaHandle = NULL;
void setupOTA()
{
if (preferences.getBool("otaEnabled", true))
{
void setupOTA() {
if (preferences.getBool("otaEnabled", true)) {
ArduinoOTA.onStart(onOTAStart);
ArduinoOTA.onProgress(onOTAProgress);
@ -18,37 +16,33 @@ void setupOTA()
ArduinoOTA.begin();
// 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);
pixels.fill(pixels.Color(0, 255, 0));
if (percentage < 100)
{
if (percentage < 100) {
pixels.setPixelColor(0, pixels.Color(0, 0, 0));
}
if (percentage < 75)
{
if (percentage < 75) {
pixels.setPixelColor(1, pixels.Color(0, 0, 0));
}
if (percentage < 50)
{
if (percentage < 50) {
pixels.setPixelColor(2, pixels.Color(0, 0, 0));
}
if (percentage < 25)
{
if (percentage < 25) {
pixels.setPixelColor(3, pixels.Color(0, 0, 0));
}
pixels.show();
}
void onOTAStart()
{
void onOTAStart() {
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);
// Stop all timers
esp_timer_stop(screenRotateTimer);
@ -68,17 +62,14 @@ void onOTAStart()
stopPriceNotify();
}
void handleOTATask(void *parameter)
{
for (;;)
{
void handleOTATask(void *parameter) {
for (;;) {
ArduinoOTA.handle(); // Allow OTA updates to occur
vTaskDelay(pdMS_TO_TICKS(2500));
}
}
void downloadUpdate()
{
void downloadUpdate() {
WiFiClientSecure client;
client.setInsecure();
HTTPClient http;
@ -87,11 +78,11 @@ void downloadUpdate()
// Send HTTP request to CoinGecko API
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();
if (httpCode == 200)
{
if (httpCode == 200) {
// WiFiClient * stream = http->getStreamPtr();
StaticJsonDocument<64> filter;
@ -102,18 +93,17 @@ void downloadUpdate()
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.println(error.c_str());
return;
}
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) {
downloadUrl = asset["browser_download_url"].as<String>();
break;
@ -122,8 +112,6 @@ void downloadUpdate()
Serial.printf("Download update from %s", downloadUrl);
// esp_http_client_config_t config = {
// .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");
Wire.end();
SPI.end();
@ -147,8 +134,7 @@ void onOTAError(ota_error_t error)
ESP.restart();
}
void onOTAComplete()
{
void onOTAComplete() {
Serial.println("\nOTA update finished");
Wire.end();
SPI.end();

View file

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

View file

@ -1,15 +1,14 @@
#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;
void taskPriceFetch(void *pvParameters)
{
void taskPriceFetch(void *pvParameters) {
WiFiClientSecure *client = new WiFiClientSecure;
client->setInsecure();
for (;;)
{
for (;;) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
HTTPClient *http = new HTTPClient();
@ -22,8 +21,7 @@ void taskPriceFetch(void *pvParameters)
// Parse JSON response and extract average price
uint usdPrice, eurPrice;
if (httpCode == 200)
{
if (httpCode == 200) {
String payload = http->getString();
StaticJsonDocument<96> doc;
deserializeJson(doc, payload);
@ -31,30 +29,28 @@ void taskPriceFetch(void *pvParameters)
eurPrice = doc["bitcoin"]["eur"].as<uint>();
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};
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
}
preferences.putUInt("lastPrice", eurPrice);
}
else
{
Serial.print(F("Error retrieving BTC/USD price (CoinGecko). HTTP status code: "));
} else {
Serial.print(
F("Error retrieving BTC/USD price (CoinGecko). HTTP status code: "));
Serial.println(httpCode);
if (httpCode == -1)
{
if (httpCode == -1) {
WiFi.reconnect();
}
}
}
}
void setupPriceFetchTask()
{
xTaskCreate(taskPriceFetch, "priceFetch", (6*1024), NULL, tskIDLE_PRIORITY, &priceFetchTaskHandle);
void setupPriceFetchTask() {
xTaskCreate(taskPriceFetch, "priceFetch", (6 * 1024), NULL, tskIDLE_PRIORITY,
&priceFetchTaskHandle);
xTaskNotifyGive(priceFetchTaskHandle);
}

View file

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

View file

@ -38,28 +38,25 @@ esp_websocket_client_handle_t clientPrice = NULL;
uint currentPrice = 30000;
unsigned long int lastPriceUpdate;
void setupPriceNotify()
{
void setupPriceNotify() {
// currentPrice = preferences.get("lastPrice", 30000);
esp_websocket_client_config_t config = {
.uri = wsServerPrice,
esp_websocket_client_config_t config = {.uri = wsServerPrice,
// .task_stack = (7*1024),
// .cert_pem = coinCapWsCert,
.user_agent = USER_AGENT
};
.user_agent = USER_AGENT};
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);
}
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;
switch (event_id)
{
switch (event_id) {
case WEBSOCKET_EVENT_CONNECTED:
Serial.println(F("Connected to CoinCap.io WebSocket"));
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);
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 (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;
if (lastPriceUpdate == 0 || (currentTime - lastPriceUpdate) > minSecPriceUpd) {
if (lastPriceUpdate == 0 ||
(currentTime - lastPriceUpdate) > minSecPriceUpd) {
// const unsigned long oldPrice = currentPrice;
currentPrice = doc["bitcoin"].as<uint>();
preferences.putUInt("lastPrice", currentPrice);
lastPriceUpdate = currentTime;
// 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};
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
}
@ -103,13 +103,9 @@ void onWebsocketPriceMessage(esp_websocket_event_data_t* event_data)
}
}
uint getPrice() {
return currentPrice;
}
uint getPrice() { return currentPrice; }
void setPrice(uint newPrice) {
currentPrice = newPrice;
}
void setPrice(uint newPrice) { currentPrice = newPrice; }
bool isPriceNotifyConnected() {
if (clientPrice == NULL)

View file

@ -1,17 +1,18 @@
#pragma once
#include <string>
#include <Arduino.h>
#include <ArduinoJson.h>
#include <string>
#include "esp_websocket_client.h"
#include "screen_handler.hpp"
#include <esp_websocket_client.h>
#include "lib/screen_handler.hpp"
// using namespace websockets;
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);
uint getPrice();

View file

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

View file

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

View file

@ -1,13 +1,13 @@
#pragma once
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <Adafruit_MCP23X17.h>
#include <ArduinoJson.h>
#include <Preferences.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <mutex>
#include "utils.hpp"
#include <utils.hpp>
extern Adafruit_MCP23X17 mcp1;
#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_CUSTOM = 99;
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 usPerMinute = 60 * usPerSecond;
@ -35,9 +37,7 @@ struct SpiRamAllocator {
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM);
}
void deallocate(void* pointer) {
heap_caps_free(pointer);
}
void deallocate(void *pointer) { heap_caps_free(pointer); }
void *reallocate(void *ptr, size_t new_size) {
return heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM);
@ -45,4 +45,3 @@ struct SpiRamAllocator {
};
using SpiRamJsonDocument = BasicJsonDocument<SpiRamAllocator>;

View file

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

View file

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

View file

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

View file

@ -2,8 +2,7 @@
#include "ESPAsyncWebServer.h"
class OneParamRewrite : public AsyncWebRewrite
{
class OneParamRewrite : public AsyncWebRewrite {
protected:
String _urlPrefix;
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 <WiFiManager.h>
#define WEBSERVER_H
#include "ESPAsyncWebServer.h"
#include "lib/config.hpp"
//char ptrTaskList[400];
uint wifiLostConnection;
extern "C" void app_main()
{
extern "C" void app_main() {
initArduino();
Serial.begin(115200);
setup();
while (true)
{
while (true) {
// vTaskList(ptrTaskList);
// Serial.println(F("**********************************"));
// Serial.println(F("Task State Prio Stack Num"));