2023-11-07 20:26:15 +00:00
|
|
|
#include "config.hpp"
|
|
|
|
|
|
|
|
#define MAX_ATTEMPTS_WIFI_CONNECTION 20
|
|
|
|
|
|
|
|
Preferences preferences;
|
2023-11-25 23:51:54 +00:00
|
|
|
Adafruit_MCP23X17 mcp1;
|
|
|
|
#ifdef IS_BTCLOCK_S3
|
|
|
|
Adafruit_MCP23X17 mcp2;
|
|
|
|
#endif
|
2024-04-27 14:48:06 +00:00
|
|
|
|
|
|
|
#ifdef HAS_FRONTLIGHT
|
|
|
|
PCA9685 flArray(PCA_I2C_ADDR);
|
|
|
|
#endif
|
|
|
|
|
2023-11-08 19:29:06 +00:00
|
|
|
std::vector<std::string> screenNameMap(SCREEN_COUNT);
|
2023-11-13 19:02:58 +00:00
|
|
|
std::mutex mcpMutex;
|
2024-04-11 22:17:40 +00:00
|
|
|
uint lastTimeSync;
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
void setup()
|
|
|
|
{
|
2024-06-07 13:18:34 +00:00
|
|
|
setupPreferences();
|
2023-11-30 21:38:01 +00:00
|
|
|
setupHardware();
|
2024-05-19 15:49:39 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
setupDisplays();
|
2024-03-10 11:35:20 +00:00
|
|
|
if (preferences.getBool("ledTestOnPower", true))
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
queueLedEffect(LED_POWER_TEST);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lockMcp(mcpMutex);
|
2024-03-10 11:35:20 +00:00
|
|
|
if (mcp1.digitalRead(3) == LOW)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
preferences.putBool("wifiConfigured", false);
|
|
|
|
preferences.remove("txPower");
|
|
|
|
|
|
|
|
WiFi.eraseAP();
|
|
|
|
queueLedEffect(LED_EFFECT_WIFI_ERASE_SETTINGS);
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
2023-11-30 21:38:01 +00:00
|
|
|
}
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2024-03-18 16:17:04 +00:00
|
|
|
{
|
|
|
|
if (mcp1.digitalRead(0) == LOW)
|
|
|
|
{
|
|
|
|
// Then loop forever to prevent anything else from writing to the screen
|
2024-05-08 21:54:17 +00:00
|
|
|
while (true)
|
|
|
|
{
|
2024-03-18 16:17:04 +00:00
|
|
|
delay(1000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
tryImprovSetup();
|
2023-11-07 20:34:26 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
setupWebserver();
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
// setupWifi();
|
2024-04-11 22:17:40 +00:00
|
|
|
syncTime();
|
2023-11-30 21:38:01 +00:00
|
|
|
finishSetup();
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
setupTasks();
|
|
|
|
setupTimers();
|
2023-11-10 12:02:05 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
xTaskCreate(setupWebsocketClients, "setupWebsocketClients", 4096, NULL,
|
|
|
|
tskIDLE_PRIORITY, NULL);
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
setupButtonTask();
|
|
|
|
setupOTA();
|
2023-11-13 16:51:10 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
waitUntilNoneBusy();
|
2024-06-07 23:00:52 +00:00
|
|
|
|
|
|
|
#ifdef HAS_FRONTLIGHT
|
|
|
|
if (!preferences.getBool("flAlwaysOn", false)) {
|
|
|
|
frontlightFadeOutAll(preferences.getUInt("flEffectDelay"), true);
|
|
|
|
flArray.allOFF();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
forceFullRefresh();
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
void tryImprovSetup()
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
WiFi.onEvent(WiFiEvent);
|
2024-03-10 11:35:20 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-08 21:54:17 +00:00
|
|
|
// if (!preferences.getBool("wifiConfigured", false))
|
2024-03-10 11:35:20 +00:00
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
|
|
|
|
queueLedEffect(LED_EFFECT_WIFI_WAIT_FOR_CONFIG);
|
|
|
|
|
|
|
|
uint8_t x_buffer[16];
|
|
|
|
uint8_t x_position = 0;
|
2023-11-12 23:33:48 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
bool buttonPress = false;
|
2023-11-08 11:18:59 +00:00
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
std::lock_guard<std::mutex> lockMcp(mcpMutex);
|
|
|
|
buttonPress = (mcp1.digitalRead(2) == LOW);
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
2023-11-30 21:38:01 +00:00
|
|
|
|
2023-11-10 12:02:05 +00:00
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
WiFiManager wm;
|
|
|
|
|
|
|
|
byte mac[6];
|
|
|
|
WiFi.macAddress(mac);
|
|
|
|
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);
|
|
|
|
|
|
|
|
// wm.setConfigPortalTimeout(preferences.getUInt("wpTimeout", 600));
|
|
|
|
wm.setWiFiAutoReconnect(false);
|
|
|
|
wm.setDebugOutput(false);
|
|
|
|
wm.setConfigPortalBlocking(true);
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
wm.setAPCallback([&](WiFiManager *wifiManager)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
// 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);
|
2024-03-10 11:35:20 +00:00
|
|
|
setFgColor(GxEPD_BLACK);
|
|
|
|
setBgColor(GxEPD_WHITE);
|
2023-11-30 21:38:01 +00:00
|
|
|
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;
|
2024-05-08 21:54:17 +00:00
|
|
|
// Set the UNIX timestamp
|
|
|
|
time_t timestamp = LAST_BUILD_TIME; // Example timestamp: March 7, 2021 00:00:00 UTC
|
|
|
|
|
|
|
|
// Convert the timestamp to a struct tm in UTC
|
|
|
|
struct tm *timeinfo = gmtime(×tamp);
|
|
|
|
|
|
|
|
// Format the date
|
|
|
|
char formattedDate[20];
|
|
|
|
strftime(formattedDate, sizeof(formattedDate), "%y-%m-%d\r\n%H:%M:%S", timeinfo);
|
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
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,
|
2024-05-08 21:54:17 +00:00
|
|
|
"*Hostname*:\r\n" + getMyHostname() + "\r\n\r\n" + "*FW build date:*\r\n" + formattedDate,
|
2023-11-30 21:38:01 +00:00
|
|
|
qrText};
|
2024-03-10 11:35:20 +00:00
|
|
|
setEpdContent(epdContent); });
|
2023-11-30 21:38:01 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
wm.setSaveConfigCallback([]()
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
preferences.putBool("wifiConfigured", true);
|
|
|
|
|
|
|
|
delay(1000);
|
|
|
|
// just restart after succes
|
2024-03-10 11:35:20 +00:00
|
|
|
ESP.restart(); });
|
2023-11-30 21:38:01 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
// esp_task_wdt_init(30, false);
|
|
|
|
// uint count = 0;
|
|
|
|
// while (WiFi.status() != WL_CONNECTED)
|
|
|
|
// {
|
|
|
|
// if (Serial.available() > 0)
|
|
|
|
// {
|
|
|
|
// uint8_t b = Serial.read();
|
|
|
|
|
|
|
|
// if (parse_improv_serial_byte(x_position, b, x_buffer,
|
|
|
|
// onImprovCommandCallback, onImprovErrorCallback))
|
|
|
|
// {
|
|
|
|
// x_buffer[x_position++] = b;
|
|
|
|
// }
|
|
|
|
// else
|
|
|
|
// {
|
|
|
|
// x_position = 0;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// count++;
|
|
|
|
|
|
|
|
// if (count > 2000000) {
|
|
|
|
// queueLedEffect(LED_EFFECT_HEARTBEAT);
|
|
|
|
// count = 0;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// esp_task_wdt_deinit();
|
|
|
|
// esp_task_wdt_reset();
|
|
|
|
}
|
2024-05-19 15:49:39 +00:00
|
|
|
setFgColor(preferences.getUInt("fgColor", isWhiteVersion() ? GxEPD_BLACK : GxEPD_WHITE));
|
|
|
|
setBgColor(preferences.getUInt("bgColor", isWhiteVersion() ? GxEPD_WHITE : GxEPD_BLACK));
|
2023-11-30 21:38:01 +00:00
|
|
|
}
|
2024-03-10 11:35:20 +00:00
|
|
|
// else
|
|
|
|
// {
|
|
|
|
|
|
|
|
// while (WiFi.status() != WL_CONNECTED)
|
|
|
|
// {
|
|
|
|
// vTaskDelay(pdMS_TO_TICKS(400));
|
|
|
|
// }
|
|
|
|
// }
|
2023-11-30 21:38:01 +00:00
|
|
|
// queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS);
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
|
|
|
|
2024-04-11 22:17:40 +00:00
|
|
|
void syncTime()
|
2024-03-10 11:35:20 +00:00
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
configTime(preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS), 0,
|
|
|
|
NTP_SERVER);
|
|
|
|
struct tm timeinfo;
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
while (!getLocalTime(&timeinfo))
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
configTime(preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS), 0,
|
|
|
|
NTP_SERVER);
|
|
|
|
delay(500);
|
|
|
|
Serial.println(F("Retry set time"));
|
|
|
|
}
|
2024-04-11 22:17:40 +00:00
|
|
|
|
|
|
|
lastTimeSync = esp_timer_get_time() / 1000000;
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
void setupPreferences()
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
preferences.begin("btclock", false);
|
2023-11-13 16:14:11 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
setFgColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR));
|
|
|
|
setBgColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR));
|
|
|
|
setBlockHeight(preferences.getUInt("blockHeight", 816000));
|
|
|
|
setPrice(preferences.getUInt("lastPrice", 30000));
|
2023-11-10 12:02:05 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
screenNameMap[SCREEN_BLOCK_HEIGHT] = "Block Height";
|
2024-03-10 19:24:55 +00:00
|
|
|
screenNameMap[SCREEN_BLOCK_FEE_RATE] = "Block Fee Rate";
|
2023-11-30 21:38:01 +00:00
|
|
|
screenNameMap[SCREEN_MSCW_TIME] = "Sats per dollar";
|
|
|
|
screenNameMap[SCREEN_BTC_TICKER] = "Ticker";
|
|
|
|
screenNameMap[SCREEN_TIME] = "Time";
|
|
|
|
screenNameMap[SCREEN_HALVING_COUNTDOWN] = "Halving countdown";
|
|
|
|
screenNameMap[SCREEN_MARKET_CAP] = "Market Cap";
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
void setupWebsocketClients(void *pvParameters)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
setupBlockNotify();
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
if (preferences.getBool("fetchEurPrice", false))
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
setupPriceFetchTask();
|
2024-03-10 11:35:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
setupPriceNotify();
|
|
|
|
}
|
2023-11-08 14:27:22 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
vTaskDelete(NULL);
|
2023-11-08 11:18:59 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
void setupTimers()
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
xTaskCreate(setupTimeUpdateTimer, "setupTimeUpdateTimer", 2048, NULL,
|
|
|
|
tskIDLE_PRIORITY, NULL);
|
|
|
|
xTaskCreate(setupScreenRotateTimer, "setupScreenRotateTimer", 2048, NULL,
|
|
|
|
tskIDLE_PRIORITY, NULL);
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
void finishSetup()
|
|
|
|
{
|
|
|
|
if (preferences.getBool("ledStatus", false))
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
restoreLedState();
|
2024-03-10 11:35:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
clearLeds();
|
|
|
|
}
|
|
|
|
}
|
2023-11-25 23:51:54 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
std::vector<std::string> getScreenNameMap() { return screenNameMap; }
|
2023-11-25 23:51:54 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
void setupMcp()
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
#ifdef IS_BTCLOCK_S3
|
|
|
|
const int mcp1AddrPins[] = {MCP1_A0_PIN, MCP1_A1_PIN, MCP1_A2_PIN};
|
|
|
|
const int mcp1AddrValues[] = {LOW, LOW, LOW};
|
2023-11-25 23:51:54 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
const int mcp2AddrPins[] = {MCP2_A0_PIN, MCP2_A1_PIN, MCP2_A2_PIN};
|
|
|
|
const int mcp2AddrValues[] = {LOW, LOW, HIGH};
|
2023-11-25 23:51:54 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
pinMode(MCP_RESET_PIN, OUTPUT);
|
|
|
|
digitalWrite(MCP_RESET_PIN, HIGH);
|
2023-11-25 23:51:54 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
for (int i = 0; i < 3; ++i)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
pinMode(mcp1AddrPins[i], OUTPUT);
|
|
|
|
digitalWrite(mcp1AddrPins[i], mcp1AddrValues[i]);
|
|
|
|
|
|
|
|
pinMode(mcp2AddrPins[i], OUTPUT);
|
|
|
|
digitalWrite(mcp2AddrPins[i], mcp2AddrValues[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
digitalWrite(MCP_RESET_PIN, LOW);
|
|
|
|
delay(30);
|
|
|
|
digitalWrite(MCP_RESET_PIN, HIGH);
|
|
|
|
#endif
|
2023-11-25 23:51:54 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
void setupHardware()
|
|
|
|
{
|
|
|
|
if (!LittleFS.begin(true))
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
Serial.println(F("An Error has occurred while mounting LittleFS"));
|
|
|
|
}
|
2023-11-20 17:59:33 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
if (!LittleFS.open("/index.html.gz", "r"))
|
|
|
|
{
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("Error loading WebUI"));
|
2023-11-30 21:38:01 +00:00
|
|
|
}
|
2023-11-17 21:09:28 +00:00
|
|
|
|
2024-05-08 21:54:17 +00:00
|
|
|
// if (!LittleFS.exists("/qr.txt"))
|
|
|
|
// {
|
|
|
|
// File f = LittleFS.open("/qr.txt", "w");
|
|
|
|
|
|
|
|
// if(f) {
|
|
|
|
|
|
|
|
// } else {
|
|
|
|
// Serial.println(F("Can't write QR to FS"));
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
setupLeds();
|
2023-11-10 12:02:05 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
WiFi.setHostname(getMyHostname().c_str());
|
2024-03-10 11:35:20 +00:00
|
|
|
if (!psramInit())
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
Serial.println(F("PSRAM not available"));
|
|
|
|
}
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
setupMcp();
|
2023-11-25 23:51:54 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
Wire.begin(I2C_SDA_PIN, I2C_SCK_PIN, 400000);
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
if (!mcp1.begin_I2C(0x20))
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
Serial.println(F("Error MCP23017"));
|
|
|
|
|
|
|
|
// while (1)
|
|
|
|
// ;
|
2024-03-10 11:35:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
pinMode(MCP_INT_PIN, INPUT_PULLUP);
|
|
|
|
mcp1.setupInterrupts(false, false, LOW);
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
mcp1.pinMode(i, INPUT_PULLUP);
|
|
|
|
mcp1.setupInterruptPin(i, LOW);
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
2023-11-30 21:38:01 +00:00
|
|
|
#ifndef IS_BTCLOCK_S3
|
2024-03-10 11:35:20 +00:00
|
|
|
for (int i = 8; i <= 14; i++)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
mcp1.pinMode(i, OUTPUT);
|
2023-11-25 23:51:54 +00:00
|
|
|
}
|
2023-11-30 21:38:01 +00:00
|
|
|
#endif
|
|
|
|
}
|
2023-11-25 23:51:54 +00:00
|
|
|
|
2024-05-19 15:49:39 +00:00
|
|
|
#ifdef IS_HW_REV_B
|
|
|
|
pinMode(39, INPUT_PULLUP);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
#ifdef IS_BTCLOCK_S3
|
2024-03-10 11:35:20 +00:00
|
|
|
if (!mcp2.begin_I2C(0x21))
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
Serial.println(F("Error MCP23017"));
|
2023-11-25 23:51:54 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
// while (1)
|
|
|
|
// ;
|
|
|
|
}
|
|
|
|
#endif
|
2024-04-27 14:48:06 +00:00
|
|
|
|
|
|
|
#ifdef HAS_FRONTLIGHT
|
|
|
|
setupFrontlight();
|
|
|
|
#endif
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
void improvGetAvailableWifiNetworks()
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
int networkNum = WiFi.scanNetworks();
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
for (int id = 0; id < networkNum; ++id)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
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);
|
2023-11-07 20:26:15 +00:00
|
|
|
improv_send_response(data);
|
2023-11-30 21:38:01 +00:00
|
|
|
}
|
|
|
|
// final response
|
|
|
|
std::vector<uint8_t> data = improv::build_rpc_response(
|
|
|
|
improv::GET_WIFI_NETWORKS, std::vector<std::string>{}, false);
|
|
|
|
improv_send_response(data);
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
bool improv_connectWifi(std::string ssid, std::string password)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
uint8_t count = 0;
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
WiFi.begin(ssid.c_str(), password.c_str());
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
while (WiFi.status() != WL_CONNECTED)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
blinkDelay(500, 2);
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
if (count > MAX_ATTEMPTS_WIFI_CONNECTION)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
WiFi.disconnect();
|
|
|
|
return false;
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
2023-11-30 21:38:01 +00:00
|
|
|
count++;
|
|
|
|
}
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
return true;
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
void onImprovErrorCallback(improv::Error err)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
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));
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
std::vector<std::string> getLocalUrl()
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
return {// URL where user can finish onboarding or use device
|
|
|
|
// Recommended to use website hosted by device
|
|
|
|
String("http://" + WiFi.localIP().toString()).c_str()};
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
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);
|
|
|
|
improv_send_response(data);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
improv_set_state(improv::State::STATE_AUTHORIZED);
|
2023-11-30 21:38:01 +00:00
|
|
|
}
|
2023-11-08 11:18:59 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
}
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
case improv::Command::WIFI_SETTINGS:
|
|
|
|
{
|
|
|
|
if (cmd.ssid.length() == 0)
|
|
|
|
{
|
|
|
|
improv_set_error(improv::Error::ERROR_INVALID_RPC);
|
|
|
|
break;
|
|
|
|
}
|
2023-11-10 12:02:05 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
improv_set_state(improv::STATE_PROVISIONING);
|
|
|
|
queueLedEffect(LED_EFFECT_WIFI_CONNECTING);
|
2023-11-14 15:55:18 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
if (improv_connectWifi(cmd.ssid, cmd.password))
|
|
|
|
{
|
|
|
|
queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS);
|
2023-11-10 12:02:05 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
// std::array<String, NUM_SCREENS> epdContent = {"S", "U", "C", "C",
|
|
|
|
// "E", "S", "S"}; setEpdContent(epdContent);
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
preferences.putBool("wifiConfigured", true);
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
improv_set_state(improv::STATE_PROVISIONED);
|
|
|
|
std::vector<uint8_t> data = improv::build_rpc_response(
|
|
|
|
improv::WIFI_SETTINGS, getLocalUrl(), false);
|
|
|
|
improv_send_response(data);
|
2023-11-30 21:38:01 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
delay(2500);
|
|
|
|
ESP.restart();
|
|
|
|
setupWebserver();
|
2023-11-30 21:56:50 +00:00
|
|
|
}
|
2024-03-10 11:35:20 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
|
2023-11-30 21:38:01 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
improv_set_state(improv::STATE_STOPPED);
|
|
|
|
improv_set_error(improv::Error::ERROR_UNABLE_TO_CONNECT);
|
2023-11-30 21:56:50 +00:00
|
|
|
}
|
2023-11-30 21:38:01 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
}
|
2023-11-30 21:56:50 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
case improv::Command::GET_DEVICE_INFO:
|
|
|
|
{
|
|
|
|
std::vector<std::string> infos = {// Firmware name
|
|
|
|
"BTClock",
|
|
|
|
// Firmware version
|
|
|
|
"1.0.0",
|
|
|
|
// Hardware chip/variant
|
|
|
|
"ESP32S3",
|
|
|
|
// Device name
|
|
|
|
"BTClock"};
|
|
|
|
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:
|
|
|
|
{
|
|
|
|
improvGetAvailableWifiNetworks();
|
|
|
|
// std::array<String, NUM_SCREENS> epdContent = {"W", "E", "B", "W", "I",
|
|
|
|
// "F", "I"}; setEpdContent(epdContent);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
improv_set_error(improv::ERROR_UNKNOWN_RPC);
|
|
|
|
return false;
|
|
|
|
}
|
2023-11-30 21:38:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
void improv_set_state(improv::State state)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'};
|
|
|
|
data.resize(11);
|
|
|
|
data[6] = improv::IMPROV_SERIAL_VERSION;
|
|
|
|
data[7] = improv::TYPE_CURRENT_STATE;
|
|
|
|
data[8] = 1;
|
|
|
|
data[9] = state;
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
uint8_t checksum = 0x00;
|
2024-03-10 11:35:20 +00:00
|
|
|
for (uint8_t d : data)
|
|
|
|
checksum += d;
|
2023-11-30 21:38:01 +00:00
|
|
|
data[10] = checksum;
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
Serial.write(data.data(), data.size());
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
void improv_send_response(std::vector<uint8_t> &response)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'};
|
|
|
|
data.resize(9);
|
|
|
|
data[6] = improv::IMPROV_SERIAL_VERSION;
|
|
|
|
data[7] = improv::TYPE_RPC_RESPONSE;
|
|
|
|
data[8] = response.size();
|
|
|
|
data.insert(data.end(), response.begin(), response.end());
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
uint8_t checksum = 0x00;
|
2024-03-10 11:35:20 +00:00
|
|
|
for (uint8_t d : data)
|
|
|
|
checksum += d;
|
2023-11-30 21:38:01 +00:00
|
|
|
data.push_back(checksum);
|
2023-11-07 20:26:15 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
Serial.write(data.data(), data.size());
|
2023-11-07 20:26:15 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
void improv_set_error(improv::Error error)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'};
|
|
|
|
data.resize(11);
|
|
|
|
data[6] = improv::IMPROV_SERIAL_VERSION;
|
|
|
|
data[7] = improv::TYPE_ERROR_STATE;
|
|
|
|
data[8] = 1;
|
|
|
|
data[9] = error;
|
2023-11-12 23:33:48 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
uint8_t checksum = 0x00;
|
2024-03-10 11:35:20 +00:00
|
|
|
for (uint8_t d : data)
|
|
|
|
checksum += d;
|
2023-11-30 21:38:01 +00:00
|
|
|
data[10] = checksum;
|
2023-11-20 17:59:33 +00:00
|
|
|
|
2023-11-30 21:38:01 +00:00
|
|
|
Serial.write(data.data(), data.size());
|
|
|
|
}
|
2023-11-12 23:33:48 +00:00
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
static bool first_connect = true;
|
|
|
|
|
|
|
|
Serial.printf("[WiFi-event] event: %d\n", event);
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
switch (event)
|
|
|
|
{
|
|
|
|
case ARDUINO_EVENT_WIFI_READY:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("WiFi interface ready"));
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_SCAN_DONE:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("Completed scan for access points"));
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_STA_START:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("WiFi client started"));
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_STA_STOP:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("WiFi clients stopped"));
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("Connected to access point"));
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
|
|
|
{
|
|
|
|
if (!first_connect)
|
|
|
|
{
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("Disconnected from WiFi access point"));
|
2023-11-30 21:56:50 +00:00
|
|
|
queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
|
2024-03-10 11:35:20 +00:00
|
|
|
uint8_t reason = info.wifi_sta_disconnected.reason;
|
|
|
|
if (reason)
|
|
|
|
Serial.printf("Disconnect reason: %s, ",
|
|
|
|
WiFi.disconnectReasonName((wifi_err_reason_t)reason));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("Authentication mode of access point has changed"));
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
|
|
|
{
|
|
|
|
Serial.print("Obtained IP address: ");
|
|
|
|
Serial.println(WiFi.localIP());
|
|
|
|
if (!first_connect)
|
|
|
|
queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS);
|
|
|
|
first_connect = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ARDUINO_EVENT_WIFI_STA_LOST_IP:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("Lost IP address and IP address is reset to 0"));
|
2024-03-10 11:35:20 +00:00
|
|
|
queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
|
|
|
|
WiFi.reconnect();
|
|
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_AP_START:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("WiFi access point started"));
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_AP_STOP:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("WiFi access point stopped"));
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("Client connected"));
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("Client disconnected"));
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("Assigned IP address to client"));
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("Received probe request"));
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_AP_GOT_IP6:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("AP IPv6 is preferred"));
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
|
2024-04-11 22:17:40 +00:00
|
|
|
Serial.println(F("STA IPv6 is preferred"));
|
2024-03-10 11:35:20 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2023-11-30 21:38:01 +00:00
|
|
|
}
|
2023-11-28 00:30:36 +00:00
|
|
|
}
|
|
|
|
|
2024-03-10 11:35:20 +00:00
|
|
|
String getMyHostname()
|
|
|
|
{
|
2023-11-30 21:38:01 +00:00
|
|
|
uint8_t mac[6];
|
|
|
|
// WiFi.macAddress(mac);
|
|
|
|
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]);
|
|
|
|
return hostname;
|
2024-04-11 22:17:40 +00:00
|
|
|
}
|
|
|
|
|
2024-05-08 21:54:17 +00:00
|
|
|
uint getLastTimeSync()
|
|
|
|
{
|
2024-04-11 22:17:40 +00:00
|
|
|
return lastTimeSync;
|
2024-04-27 14:48:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAS_FRONTLIGHT
|
2024-05-08 21:54:17 +00:00
|
|
|
void setupFrontlight()
|
|
|
|
{
|
2024-06-07 13:18:34 +00:00
|
|
|
if (!flArray.begin(PCA9685_MODE1_AUTOINCR | PCA9685_MODE1_ALLCALL, PCA9685_MODE2_TOTEMPOLE))
|
2024-05-08 21:54:17 +00:00
|
|
|
{
|
|
|
|
Serial.println(F("FL driver error"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Serial.println(F("FL driver active"));
|
2024-06-07 13:18:34 +00:00
|
|
|
|
2024-05-08 21:54:17 +00:00
|
|
|
if (!preferences.isKey("flMaxBrightness"))
|
|
|
|
{
|
2024-06-07 13:18:34 +00:00
|
|
|
preferences.putUInt("flMaxBrightness", 2048);
|
|
|
|
}
|
2024-06-07 23:00:52 +00:00
|
|
|
if (!preferences.isKey("flEffectDelay"))
|
|
|
|
{
|
|
|
|
preferences.putUInt("flEffectDelay", 5);
|
|
|
|
}
|
2024-06-07 13:18:34 +00:00
|
|
|
|
2024-06-07 23:00:52 +00:00
|
|
|
if (!preferences.isKey("flFlashOnUpd"))
|
|
|
|
{
|
|
|
|
preferences.putBool("flFlashOnUpd", false);
|
2024-04-27 14:48:06 +00:00
|
|
|
}
|
2024-06-07 23:00:52 +00:00
|
|
|
|
|
|
|
frontlightFadeInAll(preferences.getUInt("flEffectDelay"), true);
|
2024-04-27 14:48:06 +00:00
|
|
|
}
|
2024-05-18 21:00:08 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
String getHwRev() {
|
|
|
|
#ifndef HW_REV
|
|
|
|
return "REV_0";
|
|
|
|
#else
|
|
|
|
return HW_REV;
|
|
|
|
#endif
|
2024-05-19 15:49:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool isWhiteVersion() {
|
|
|
|
#ifdef IS_HW_REV_B
|
|
|
|
return digitalRead(39);
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
2024-05-18 21:00:08 +00:00
|
|
|
}
|