LEDs and buttons working

This commit is contained in:
Djuri 2023-11-08 12:18:59 +01:00
parent 4f2fbd8a36
commit 91fd921e2e
33 changed files with 3877 additions and 136 deletions

View file

@ -1,8 +1,7 @@
#include "block_notify.hpp"
const char *wsServer = "wss://mempool.space/api/v1/ws";
// WebsocketsClient client;
esp_websocket_client_handle_t client;
char *wsServer;
esp_websocket_client_handle_t blockNotifyClient = NULL;
unsigned long int currentBlockHeight;
void setupBlockNotify()
@ -36,15 +35,17 @@ void setupBlockNotify()
xTaskNotifyGive(blockUpdateTaskHandle);
}
// std::strcpy(wsServer, String("wss://" + mempoolInstance + "/api/v1/ws").c_str());
esp_websocket_client_config_t config = {
.uri = "wss://mempool.bitcoin.nl/api/v1/ws",
};
Serial.printf("Connecting to %s\r\n", config.uri);
client = esp_websocket_client_init(&config);
esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY, onWebsocketEvent, client);
esp_websocket_client_start(client);
blockNotifyClient = esp_websocket_client_init(&config);
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)
@ -58,7 +59,7 @@ void onWebsocketEvent(void *handler_args, esp_event_base_t base, int32_t event_i
Serial.println("Connected to Mempool.space WebSocket");
sub = "{\"action\": \"want\", \"data\":[\"blocks\"]}";
if (esp_websocket_client_send_text(client, 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("Mempool.space WS Block Subscribe Error");
}
@ -92,8 +93,10 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data)
Serial.print("New block found: ");
Serial.println(block["height"].as<long>());
if (blockUpdateTaskHandle != nullptr)
if (blockUpdateTaskHandle != nullptr) {
xTaskNotifyGive(blockUpdateTaskHandle);
queueLedEffect(LED_FLASH_BLOCK_NOTIFY);
}
}
doc.clear();
@ -102,4 +105,10 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data)
unsigned long getBlockHeight()
{
return currentBlockHeight;
}
bool isBlockNotifyConnected() {
if (blockNotifyClient == NULL)
return false;
return esp_websocket_client_is_connected(blockNotifyClient);
}

View file

@ -1,5 +1,6 @@
#pragma once
#include <cstring>
#include <string>
#include <Arduino.h>
#include <HTTPClient.h>
@ -8,6 +9,7 @@
#include "esp_websocket_client.h"
#include "screen_handler.hpp"
#include "led_handler.hpp"
//using namespace websockets;
@ -16,4 +18,5 @@ void setupBlockNotify();
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);
unsigned long getBlockHeight();
unsigned long getBlockHeight();
bool isBlockNotifyConnected();

View file

@ -18,22 +18,21 @@ void buttonTask(void *parameter)
{
uint pin = mcp.getLastInterruptPin();
Serial.printf("Button pressed: %d", pin);
// switch (pin)
// {
// case 3:
// toggleScreenTimer();
// break;
// case 2:
// nextScreen();
// break;
// case 1:
// previousScreen();
// break;
// case 0:
// showNetworkSettings();
// break;
// }
switch (pin)
{
case 3:
toggleTimerActive();
break;
case 2:
nextScreen();
break;
case 1:
previousScreen();
break;
case 0:
showSystemStatusScreen();
break;
}
}
mcp.clearInterrupts();
// Very ugly, but for some reason this is necessary

View file

@ -4,6 +4,7 @@
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "shared.hpp"
#include "screen_handler.hpp"
extern TaskHandle_t buttonTaskHandle;

View file

@ -1,27 +1,22 @@
#include "config.hpp"
#ifndef NEOPIXEL_PIN
#define NEOPIXEL_PIN 34
#endif
#ifndef NEOPIXEL_COUNT
#define NEOPIXEL_COUNT 4
#endif
#define MAX_ATTEMPTS_WIFI_CONNECTION 20
Preferences preferences;
Adafruit_MCP23X17 mcp;
Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
std::map<int, std::string> screenNameMap;
void setup()
{
setupHardware();
if (mcp.digitalRead(3) == LOW) {
if (mcp.digitalRead(3) == LOW)
{
WiFi.eraseAP();
blinkDelay(100, 3);
}
setupDisplays();
tryImprovSetup();
@ -47,9 +42,9 @@ void tryImprovSetup()
// blinkDelay(100, 3);
// }
// else
// {
// WiFi.begin();
// }
{
WiFi.begin();
}
uint8_t x_buffer[16];
uint8_t x_position = 0;
@ -69,7 +64,8 @@ void tryImprovSetup()
x_position = 0;
}
}
// vTaskDelay(1);
vTaskDelay(1 / portTICK_PERIOD_MS);
}
}
@ -91,6 +87,15 @@ void setupTime()
void setupPreferences()
{
preferences.begin("btclock", false);
setFgColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR));
setBgColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR));
screenNameMap = {{SCREEN_BLOCK_HEIGHT, "Block Height"},
{SCREEN_MSCW_TIME, "Sats per dollar"},
{SCREEN_BTC_TICKER, "Ticker"},
{SCREEN_TIME, "Time"},
{SCREEN_HALVING_COUNTDOWN, "Halving countdown"}};
}
void setupWebsocketClients()
@ -107,18 +112,16 @@ void setupTimers()
void finishSetup()
{
pixels.clear();
pixels.show();
clearLeds();
}
std::map<int, std::string> getScreenNameMap() {
return screenNameMap;
}
void setupHardware()
{
pixels.begin();
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
pixels.setPixelColor(1, pixels.Color(0, 255, 0));
pixels.setPixelColor(2, pixels.Color(0, 0, 255));
pixels.setPixelColor(3, pixels.Color(255, 255, 255));
pixels.show();
setupLeds();
if (psramInit())
{
@ -129,7 +132,6 @@ void setupHardware()
Serial.println(F("PSRAM not available"));
}
Wire.begin(35, 36, 400000);
if (!mcp.begin_I2C(0x20))
@ -153,7 +155,6 @@ void setupHardware()
{
mcp.pinMode(i, OUTPUT);
}
}
}
@ -194,41 +195,20 @@ bool improv_connectWifi(std::string ssid, std::string password)
return true;
}
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));
pixels.setPixelColor(2, pixels.Color(255, 0, 0));
pixels.setPixelColor(3, pixels.Color(0, 255, 0));
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
pixels.setPixelColor(0, pixels.Color(255, 255, 0));
pixels.setPixelColor(1, pixels.Color(0, 255, 255));
pixels.setPixelColor(2, pixels.Color(255, 255, 0));
pixels.setPixelColor(3, pixels.Color(0, 255, 255));
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
}
pixels.clear();
pixels.show();
}
void onImprovErrorCallback(improv::Error err)
{
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
pixels.setPixelColor(1, pixels.Color(255, 0, 0));
pixels.setPixelColor(2, pixels.Color(255, 0, 0));
pixels.setPixelColor(3, pixels.Color(255, 0, 0));
pixels.show();
vTaskDelay(pdMS_TO_TICKS(100));
blinkDelayColor(100, 1, 255,0,0);
// pixels.setPixelColor(0, pixels.Color(255, 0, 0));
// pixels.setPixelColor(1, pixels.Color(255, 0, 0));
// pixels.setPixelColor(2, pixels.Color(255, 0, 0));
// pixels.setPixelColor(3, pixels.Color(255, 0, 0));
// pixels.show();
// vTaskDelay(pdMS_TO_TICKS(100));
pixels.clear();
pixels.show();
vTaskDelay(pdMS_TO_TICKS(100));
// pixels.clear();
// pixels.show();
// vTaskDelay(pdMS_TO_TICKS(100));
}
std::vector<std::string> getLocalUrl()
@ -272,7 +252,7 @@ bool onImprovCommandCallback(improv::ImprovCommand cmd)
if (improv_connectWifi(cmd.ssid, cmd.password))
{
blinkDelay(100, 3);
// std::array<String, NUM_SCREENS> epdContent = {"S", "U", "C", "C", "E", "S", "S"};
// setEpdContent(epdContent);

View file

@ -2,7 +2,6 @@
#include <WiFiClientSecure.h>
#include <Preferences.h>
#include <Adafruit_MCP23X17.h>
#include <Adafruit_NeoPixel.h>
#include "shared.hpp"
#include <esp_system.h>
@ -16,6 +15,7 @@
#include "lib/block_notify.hpp"
#include "lib/price_notify.hpp"
#include "lib/button_handler.hpp"
#include "lib/led_handler.hpp"
#define NTP_SERVER "pool.ntp.org"
#define DEFAULT_MEMPOOL_INSTANCE "mempool.space"
@ -23,6 +23,9 @@
#define USER_AGENT "BTClock/2.0"
#define MCP_DEV_ADDR 0x20
#define DEFAULT_FG_COLOR GxEPD_WHITE
#define DEFAULT_BG_COLOR GxEPD_BLACK
#define BITCOIND_HOST ""
#define BITCOIND_PORT 8332
#define BITCOIND_RPC_USER ""
@ -36,6 +39,7 @@ void setupHardware();
void tryImprovSetup();
void setupTimers();
void finishSetup();
std::map<int, std::string> getScreenNameMap();
std::vector<std::string> getLocalUrl();
bool improv_connectWifi(std::string ssid, std::string password);
@ -45,4 +49,3 @@ void onImprovErrorCallback(improv::Error err);
void improv_set_state(improv::State state);
void improv_send_response(std::vector<uint8_t> &response);
void improv_set_error(improv::Error error);
void blinkDelay(int d, int times);

View file

@ -76,7 +76,7 @@ void setupDisplays()
int *taskParam = new int;
*taskParam = i;
xTaskCreate(updateDisplay, "EpdUpd" + char(i), 4096, taskParam, 1, &tasks[i]); // create task
xTaskCreate(updateDisplay, "EpdUpd" + char(i), 4096, taskParam, tskIDLE_PRIORITY, &tasks[i]); // create task
}
epdContent = {"B",
@ -91,7 +91,7 @@ void setupDisplays()
xTaskNotifyGive(tasks[i]);
}
xTaskCreate(taskEpd, "epd_task", 2048, NULL, 1, NULL);
xTaskCreate(taskEpd, "epd_task", 2048, NULL, tskIDLE_PRIORITY, NULL);
}
void taskEpd(void *pvParameters)

View file

@ -1,18 +1,216 @@
#include "led_handler.hpp"
TaskHandle_t ledTaskHandle = NULL;
QueueHandle_t ledTaskQueue = NULL;
Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
const TickType_t debounceDelay = pdMS_TO_TICKS(50);
uint32_t notificationValue;
unsigned long ledTaskParams;
void ledTask(void *parameter)
{
while (1)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
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++)
{
oldLights[i] = pixels.getPixelColor(i);
}
switch (ledTaskParams)
{
case LED_FLASH_ERROR:
blinkDelayColor(250, 3, 255, 0, 0);
break;
case LED_FLASH_SUCCESS:
blinkDelayColor(250, 3, 0, 255, 0);
break;
case LED_FLASH_UPDATE:
break;
case LED_FLASH_BLOCK_NOTIFY:
blinkDelayTwoColor(250, 3, pixels.Color(224, 67, 0), pixels.Color(8, 2, 0));
break;
case LED_EFFECT_PAUSE_TIMER:
for (int i = NEOPIXEL_COUNT; i >= 0; i--)
{
for (int j = NEOPIXEL_COUNT; j >= 0; j--)
{
uint32_t c = pixels.Color(0, 0, 0);
if (i == j)
c = pixels.Color(0, 255, 0);
pixels.setPixelColor(j, c);
}
pixels.show();
delay(100);
}
delay(900);
pixels.clear();
pixels.show();
break;
case LED_EFFECT_START_TIMER:
pixels.clear();
pixels.setPixelColor(NEOPIXEL_COUNT, pixels.Color(0, 255, 0));
pixels.show();
delay(900);
for (int i = NEOPIXEL_COUNT; i--; i > 0)
{
for (int j = NEOPIXEL_COUNT; j--; j > 0)
{
uint32_t c = pixels.Color(0, 0, 0);
if (i == j)
c = pixels.Color(0, 255, 0);
pixels.setPixelColor(j, c);
}
pixels.show();
delay(100);
}
pixels.clear();
pixels.show();
break;
}
// revert to previous state
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
pixels.setPixelColor(i, oldLights[i]);
}
pixels.show();
}
}
}
}
void setupLeds()
{
pixels.begin();
pixels.setBrightness(preferences.getUInt("ledBrightness", 128));
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
pixels.setPixelColor(1, pixels.Color(0, 255, 0));
pixels.setPixelColor(2, pixels.Color(0, 0, 255));
pixels.setPixelColor(3, pixels.Color(255, 255, 255));
pixels.show();
setupLedTask();
}
void setupLedTask()
{
xTaskCreate(ledTask, "LedTask", 4096, NULL, tskIDLE_PRIORITY, &ledTaskHandle); // Create the FreeRTOS task
ledTaskQueue = xQueueCreate(10, sizeof(unsigned long));
xTaskCreate(ledTask, "LedTask", 4096, NULL, tskIDLE_PRIORITY, &ledTaskHandle);
}
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));
pixels.setPixelColor(2, pixels.Color(255, 0, 0));
pixels.setPixelColor(3, pixels.Color(0, 255, 0));
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
pixels.setPixelColor(0, pixels.Color(255, 255, 0));
pixels.setPixelColor(1, pixels.Color(0, 255, 255));
pixels.setPixelColor(2, pixels.Color(255, 255, 0));
pixels.setPixelColor(3, pixels.Color(0, 255, 255));
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
}
pixels.clear();
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++)
{
pixels.setPixelColor(i, pixels.Color(r, g, b));
}
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
pixels.clear();
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
}
pixels.clear();
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++)
{
pixels.setPixelColor(i, c1);
}
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
pixels.setPixelColor(i, c2);
}
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
}
pixels.clear();
pixels.show();
}
void clearLeds()
{
pixels.clear();
pixels.show();
}
void setLights(int r, int g, int b)
{
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
pixels.setPixelColor(i, pixels.Color(r, g, b));
}
pixels.show();
}
QueueHandle_t getLedTaskQueue()
{
return ledTaskQueue;
}
bool queueLedEffect(uint effect)
{
if (ledTaskQueue == NULL)
{
return false;
}
unsigned long flashType = effect;
xQueueSend(ledTaskQueue, &flashType, portMAX_DELAY);
}

View file

@ -6,7 +6,33 @@
#include <Adafruit_NeoPixel.h>
#include "shared.hpp"
#ifndef NEOPIXEL_PIN
#define NEOPIXEL_PIN 34
#endif
#ifndef NEOPIXEL_COUNT
#define NEOPIXEL_COUNT 4
#endif
typedef struct {
int flashType;
} LedTaskParameters;
const int LED_FLASH_ERROR = 0;
const int LED_FLASH_SUCCESS = 1;
const int LED_FLASH_UPDATE = 2;
const int LED_FLASH_BLOCK_NOTIFY = 3;
const int LED_EFFECT_START_TIMER = 4;
const int LED_EFFECT_PAUSE_TIMER = 5;
extern TaskHandle_t ledTaskHandle;
void ledTask(void *pvParameters);
void setupLeds();
void setupLedTask();
void blinkDelay(int d, int times);
void blinkDelayColor(int d, int times, uint r, uint g, uint b);
void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2);
void clearLeds();
QueueHandle_t getLedTaskQueue();
bool queueLedEffect(uint effect);
void setLights(int r, int g, int b);

View file

@ -2,7 +2,7 @@
const char *wsServerPrice = "wss://ws.coincap.io/prices?assets=bitcoin";
// WebsocketsClient client;
esp_websocket_client_handle_t clientPrice;
esp_websocket_client_handle_t clientPrice = NULL;
unsigned long int currentPrice;
void setupPriceNotify()
@ -46,7 +46,6 @@ void onWebsocketPriceMessage(esp_websocket_event_data_t* event_data)
if (doc.containsKey("bitcoin")) {
if (currentPrice != doc["bitcoin"].as<long>()) {
// Serial.printf("New price %lu\r\n", currentPrice);
const unsigned long oldPrice = currentPrice;
currentPrice = doc["bitcoin"].as<long>();
@ -61,4 +60,10 @@ void onWebsocketPriceMessage(esp_websocket_event_data_t* event_data)
unsigned long getPrice() {
return currentPrice;
}
bool isPriceNotifyConnected() {
if (clientPrice == NULL)
return false;
return esp_websocket_client_is_connected(clientPrice);
}

View file

@ -14,4 +14,5 @@ void setupPriceNotify();
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);
unsigned long getPrice();
unsigned long getPrice();
bool isPriceNotifyConnected();

View file

@ -61,7 +61,16 @@ void taskScreenRotate(void *pvParameters)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
setCurrentScreen((currentScreen+1) % 5);
int nextScreen = (currentScreen+ 1) % 5;
String key = "screen" + String(nextScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true))
{
nextScreen = (nextScreen + 1) % 5;
key = "screen" + String(nextScreen) + "Visible";
}
setCurrentScreen(nextScreen);
}
}
@ -143,20 +152,6 @@ void taskTimeUpdate(void *pvParameters)
}
}
const char* int64_to_iso8601(int64_t timestamp) {
time_t seconds = timestamp / 1000000; // Convert microseconds to seconds
struct tm timeinfo;
gmtime_r(&seconds, &timeinfo);
// Define a buffer to store the formatted time string
static char iso8601[21]; // ISO 8601 time string has the format "YYYY-MM-DDTHH:MM:SSZ"
// Format the time into the buffer
strftime(iso8601, sizeof(iso8601), "%Y-%m-%dT%H:%M:%SZ", &timeinfo);
return iso8601;
}
void IRAM_ATTR minuteTimerISR(void *arg)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
@ -217,11 +212,8 @@ void setupScreenRotateTimer(void *pvParameters)
.name = "screen_rotate_timer"};
esp_timer_create(&screenRotateTimerConfig, &screenRotateTimer);
esp_timer_start_periodic(screenRotateTimer, getTimerSeconds() * usPerSecond);
Serial.println("Set up Screen Rotate Timer");
vTaskDelete(NULL);
}
@ -240,13 +232,19 @@ void setTimerActive(bool status)
if (status)
{
esp_timer_start_periodic(screenRotateTimer, getTimerSeconds() * usPerSecond);
queueLedEffect(LED_EFFECT_START_TIMER);
}
else
{
esp_timer_stop(screenRotateTimer);
queueLedEffect(LED_EFFECT_PAUSE_TIMER);
}
}
void toggleTimerActive() {
setTimerActive(!isTimerActive());
}
uint getCurrentScreen()
{
return currentScreen;
@ -275,4 +273,56 @@ void setCurrentScreen(uint newScreen)
xTaskNotifyGive(priceUpdateTaskHandle);
break;
}
}
void nextScreen()
{
int newCurrentScreen = (getCurrentScreen() + 1) % SCREEN_COUNT;
String key = "screen" + String(newCurrentScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true))
{
newCurrentScreen = (newCurrentScreen + 1) % SCREEN_COUNT;
key = "screen" + String(newCurrentScreen) + "Visible";
}
setCurrentScreen(newCurrentScreen);
}
void previousScreen()
{
int newCurrentScreen = modulo(getCurrentScreen() - 1, SCREEN_COUNT);
String key = "screen" + String(newCurrentScreen) + "Visible";
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 = {"", "", "", "", "", "", ""};
String ipAddr = WiFi.localIP().toString();
String subNet = WiFi.subnetMask().toString();
sysStatusEpdContent[0] = "IP/Subnet";
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('.'));
ipAddrPos = ipAddr.indexOf('.') + 1;
subnetPos = subNet.indexOf('.') + 1;
ipAddr = ipAddr.substring(ipAddrPos);
subNet = subNet.substring(subnetPos);
}
sysStatusEpdContent[NUM_SCREENS-2] = "RAM/Status";
sysStatusEpdContent[NUM_SCREENS-1] = String((int)round(ESP.getFreeHeap()/1024)) + "/" + (int)round(ESP.getHeapSize()/1024);
setCurrentScreen(SCREEN_CUSTOM);
setEpdContent(sysStatusEpdContent);
}

View file

@ -16,6 +16,10 @@ extern TaskHandle_t taskScreenRotateTaskHandle;
uint getCurrentScreen();
void setCurrentScreen(uint newScreen);
void nextScreen();
void previousScreen();
void showSystemStatusScreen();
void setupTimeUpdateTimer(void *pvParameters);
void setupScreenRotateTimer(void *pvParameters);
@ -31,7 +35,6 @@ void taskScreenRotate(void *pvParameters);
uint getTimerSeconds();
bool isTimerActive();
void setTimerActive(bool status);
void toggleTimerActive();
void setupTasks();
const char* int64_to_iso8601(int64_t timestamp);

View file

@ -3,6 +3,7 @@
#include <Adafruit_MCP23X17.h>
#include <ArduinoJson.h>
#include <Preferences.h>
#include "utils.hpp"
extern Adafruit_MCP23X17 mcp;
extern Preferences preferences;
@ -15,6 +16,7 @@ const PROGMEM int SCREEN_HALVING_COUNTDOWN = 4;
const PROGMEM int SCREEN_COUNTDOWN = 98;
const PROGMEM int SCREEN_CUSTOM = 99;
const PROGMEM int screens[5] = { SCREEN_BLOCK_HEIGHT, SCREEN_MSCW_TIME, SCREEN_BTC_TICKER, SCREEN_TIME, SCREEN_HALVING_COUNTDOWN };
const int SCREEN_COUNT = 5;
struct SpiRamAllocator {
void* allocate(size_t size) {

6
src/lib/utils.cpp Normal file
View file

@ -0,0 +1,6 @@
#include "utils.hpp"
int modulo(int x, int N)
{
return (x % N + N) % N;
}

1
src/lib/utils.hpp Normal file
View file

@ -0,0 +1 @@
int modulo(int x,int N);

View file

@ -28,6 +28,10 @@ void setupWebserver()
server.on("/api/show/screen", HTTP_GET, onApiShowScreen);
server.on("/api/show/text", HTTP_GET, onApiShowText);
server.on("/api/lights/off", HTTP_GET, onApiLightsOff);
server.on("/api/lights/color", 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/show/screen/{s}", "/api/show/screen?s={s}"));
@ -59,6 +63,10 @@ void onApiStatus(AsyncWebServerRequest *request)
root["espFreePsram"] = ESP.getFreePsram();
root["espPsramSize"] = ESP.getPsramSize();
JsonObject conStatus = root.createNestedObject("connectionStatus");
conStatus["price"] = isPriceNotifyConnected();
conStatus["blocks"] = isBlockNotifyConnected();
JsonArray data = root.createNestedArray("data");
JsonArray rendered = root.createNestedArray("rendered");
String epdContent[NUM_SCREENS];
@ -170,14 +178,16 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
#endif
JsonArray screens = root.createNestedArray("screens");
// for (int i = 0; i < screenNameMap.size(); i++)
// {
// JsonObject o = screens.createNestedObject();
// String key = "screen" + String(i) + "Visible";
// o["id"] = i;
// o["name"] = screenNameMap[i];
// o["enabled"] = preferences.getBool(key.c_str(), true);
// }
std::map<int, std::string> screenNameMap = getScreenNameMap();
for (int i = 0; i < screenNameMap.size(); i++)
{
JsonObject o = screens.createNestedObject();
String key = "screen" + String(i) + "Visible";
o["id"] = i;
o["name"] = screenNameMap[i];
o["enabled"] = preferences.getBool(key.c_str(), true);
}
AsyncResponseStream *response = request->beginResponseStream("application/json");
serializeJson(root, *response);
@ -274,21 +284,23 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
settingsChanged = true;
}
// 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))
// {
// AsyncWebParameter *screenParam = request->getParam(key, true);
// visible = screenParam->value().toInt();
// }
// Serial.print("Setting screen " + String(i) + " to ");
// Serial.println(visible);
std::map<int, std::string> screenNameMap = getScreenNameMap();
// preferences.putBool(prefKey.c_str(), visible);
// }
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))
{
AsyncWebParameter *screenParam = request->getParam(key, true);
visible = screenParam->value().toInt();
}
Serial.print("Setting screen " + String(i) + " to ");
Serial.println(visible);
preferences.putBool(prefKey.c_str(), visible);
}
if (request->hasParam("tzOffset", true))
{
@ -339,7 +351,7 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
request->send(200);
if (settingsChanged)
{
//flashTemporaryLights(0, 255, 0);
queueLedEffect(LED_FLASH_SUCCESS);
Serial.println(F("Settings changed"));
}
@ -361,6 +373,22 @@ void onApiSystemStatus(AsyncWebServerRequest *request)
request->send(response);
}
void onApiLightsOff(AsyncWebServerRequest *request)
{
setLights(0, 0, 0);
request->send(200);
}
void onApiLightsSetColor(AsyncWebServerRequest *request)
{
String rgbColor = request->pathArg(0);
uint r, g, b;
sscanf(rgbColor.c_str(), "%02x%02x%02x", &r, &g, &b);
setLights(r, g, b);
request->send(200, "text/plain", rgbColor);
}
void onIndex(AsyncWebServerRequest *request) { request->send(LittleFS, "/index.html", String(), false); }
void onNotFound(AsyncWebServerRequest *request)

View file

@ -7,7 +7,7 @@
#include "lib/block_notify.hpp"
#include "lib/price_notify.hpp"
#include "lib/screen_handler.hpp"
#include "lib/led_handler.hpp"
#include "webserver/OneParamRewrite.hpp"
@ -25,6 +25,10 @@ void onApiActionTimerRestart(AsyncWebServerRequest *request);
void onApiSettingsGet(AsyncWebServerRequest *request);
void onApiSettingsPost(AsyncWebServerRequest *request);
void onApiLightsOff(AsyncWebServerRequest *request);
void onApiLightsSetColor(AsyncWebServerRequest *request);
void onApiRestart(AsyncWebServerRequest *request);
void onIndex(AsyncWebServerRequest *request);