1440 lines
44 KiB
C++
1440 lines
44 KiB
C++
#define IS_VALID_DETECT 0xa00ab00bc00bd00d;
|
|
|
|
#include "Arduino.h"
|
|
#include "esp_crt_bundle.h"
|
|
#include "esp_ota_ops.h"
|
|
#include "esp_http_client.h"
|
|
#include "esp_https_ota.h"
|
|
#include "esp_task_wdt.h"
|
|
#ifdef CONFIG_HEAP_TASK_TRACKING
|
|
#include "esp_heap_task_info.h"
|
|
#include "esp_heap_caps.h"
|
|
#endif
|
|
#include "Config.h"
|
|
#include "esp32-hal-log.h"
|
|
#include "hal/wdt_hal.h"
|
|
#include "esp_chip_info.h"
|
|
#include "esp_netif_sntp.h"
|
|
#include "esp_core_dump.h"
|
|
#include "FS.h"
|
|
#include "SPIFFS.h"
|
|
//#include <ESPmDNS.h>
|
|
#ifdef NUKI_HUB_HTTPS_SERVER
|
|
bool nuki_hub_https_server_enabled = true;
|
|
#else
|
|
bool nuki_hub_https_server_enabled = false;
|
|
#endif
|
|
#if defined(CONFIG_SOC_SPIRAM_SUPPORTED) && defined(CONFIG_SPIRAM)
|
|
#include "esp_psram.h"
|
|
#endif
|
|
|
|
#ifndef NUKI_HUB_UPDATER
|
|
#include "SerialReader.h"
|
|
#include "NukiWrapper.h"
|
|
#include "NukiNetworkLock.h"
|
|
#include "NukiOpenerWrapper.h"
|
|
#include "Gpio.h"
|
|
#include "Gpio.h"
|
|
#include "CharBuffer.h"
|
|
#include "NukiDeviceId.h"
|
|
#include "WebCfgServer.h"
|
|
#include "Logger.h"
|
|
#include "PreferencesKeys.h"
|
|
#include "RestartReason.h"
|
|
#include "EspMillis.h"
|
|
#include "NimBLEDevice.h"
|
|
#include "ImportExport.h"
|
|
|
|
/*
|
|
#ifdef DEBUG_NUKIHUB
|
|
#include <WString.h>
|
|
#include <MycilaWebSerial.h>
|
|
#endif
|
|
*/
|
|
|
|
NukiNetworkLock* networkLock = nullptr;
|
|
NukiNetworkOpener* networkOpener = nullptr;
|
|
BleScanner::Scanner* bleScanner = nullptr;
|
|
NukiWrapper* nuki = nullptr;
|
|
NukiOfficial* nukiOfficial = nullptr;
|
|
NukiOpenerWrapper* nukiOpener = nullptr;
|
|
NukiDeviceId* deviceIdLock = nullptr;
|
|
NukiDeviceId* deviceIdOpener = nullptr;
|
|
Gpio* gpio = nullptr;
|
|
SerialReader* serialReader = nullptr;
|
|
|
|
bool bleDone = false;
|
|
bool lockEnabled = false;
|
|
bool openerEnabled = false;
|
|
bool wifiConnected = false;
|
|
bool rebootLock = false;
|
|
uint8_t lockRestartControllerCount = 0;
|
|
uint8_t openerRestartControllerCount = 0;
|
|
char16_t buffer_size = CHAR_BUFFER_SIZE;
|
|
|
|
TaskHandle_t nukiTaskHandle = nullptr;
|
|
|
|
int64_t restartTs = (pow(2,63) - (5 * 1000 * 60000)) / 1000;
|
|
|
|
#else
|
|
#include "../../src/WebCfgServer.h"
|
|
#include "../../src/Logger.h"
|
|
#include "../../src/PreferencesKeys.h"
|
|
#include "../../src/RestartReason.h"
|
|
#include "../../src/NukiNetwork.h"
|
|
#include "../../src/EspMillis.h"
|
|
#include "../../src/ImportExport.h"
|
|
|
|
int64_t restartTs = 10 * 60 * 1000;
|
|
|
|
#endif
|
|
|
|
char log_print_buffer[1024];
|
|
|
|
PsychicHttpServer* psychicServer = nullptr;
|
|
PsychicHttpServer* psychicServerRedirect = nullptr;
|
|
PsychicHttpsServer* psychicSSLServer = nullptr;
|
|
NukiNetwork* network = nullptr;
|
|
WebCfgServer* webCfgServer = nullptr;
|
|
WebCfgServer* webCfgServerSSL = nullptr;
|
|
Preferences* preferences = nullptr;
|
|
ImportExport* importExport = nullptr;
|
|
|
|
RTC_NOINIT_ATTR int espRunning;
|
|
RTC_NOINIT_ATTR int restartReason;
|
|
RTC_NOINIT_ATTR uint64_t restartReasonValidDetect;
|
|
RTC_NOINIT_ATTR bool rebuildGpioRequested;
|
|
RTC_NOINIT_ATTR uint64_t bootloopValidDetect;
|
|
RTC_NOINIT_ATTR int8_t bootloopCounter;
|
|
RTC_NOINIT_ATTR bool forceEnableWebServer;
|
|
RTC_NOINIT_ATTR bool disableNetwork;
|
|
RTC_NOINIT_ATTR bool wifiFallback;
|
|
RTC_NOINIT_ATTR bool ethCriticalFailure;
|
|
|
|
bool coredumpPrinted = true;
|
|
bool timeSynced = false;
|
|
bool webStarted = false;
|
|
bool webSSLStarted = false;
|
|
bool lockStarted = false;
|
|
bool openerStarted = false;
|
|
bool bleScannerStarted = false;
|
|
uint8_t partitionType = -1;
|
|
|
|
int lastHTTPeventId = -1;
|
|
bool doOta = false;
|
|
bool restartReason_isValid;
|
|
RestartReason currentRestartReason = RestartReason::NotApplicable;
|
|
|
|
TaskHandle_t otaTaskHandle = nullptr;
|
|
TaskHandle_t networkTaskHandle = nullptr;
|
|
|
|
ssize_t write_fn(void* cookie, const char* buf, ssize_t size)
|
|
{
|
|
Log->write((uint8_t *)buf, (size_t)size);
|
|
return size;
|
|
}
|
|
|
|
void ets_putc_handler(char c)
|
|
{
|
|
static char buf[1024];
|
|
static size_t buf_pos = 0;
|
|
buf[buf_pos] = c;
|
|
buf_pos++;
|
|
if (c == '\n' || buf_pos == sizeof(buf))
|
|
{
|
|
write_fn(NULL, buf, buf_pos);
|
|
buf_pos = 0;
|
|
}
|
|
}
|
|
|
|
int _log_vprintf(const char *fmt, va_list args)
|
|
{
|
|
int ret = vsnprintf(log_print_buffer, sizeof(log_print_buffer), fmt, args);
|
|
if (ret >= 0)
|
|
{
|
|
Log->write((uint8_t *)log_print_buffer, (size_t)ret);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void setReroute()
|
|
{
|
|
esp_log_set_vprintf(_log_vprintf);
|
|
|
|
#ifdef DEBUG_NUKIHUB
|
|
esp_log_level_set("*", ESP_LOG_DEBUG);
|
|
esp_log_level_set("nvs", ESP_LOG_INFO);
|
|
esp_log_level_set("wifi", ESP_LOG_INFO);
|
|
#else
|
|
/*
|
|
esp_log_level_set("*", ESP_LOG_NONE);
|
|
esp_log_level_set("httpd", ESP_LOG_ERROR);
|
|
esp_log_level_set("httpd_sess", ESP_LOG_ERROR);
|
|
esp_log_level_set("httpd_parse", ESP_LOG_ERROR);
|
|
esp_log_level_set("httpd_txrx", ESP_LOG_ERROR);
|
|
esp_log_level_set("httpd_uri", ESP_LOG_ERROR);
|
|
esp_log_level_set("event", ESP_LOG_ERROR);
|
|
esp_log_level_set("psychic", ESP_LOG_ERROR);
|
|
esp_log_level_set("ARDUINO", ESP_LOG_DEBUG);
|
|
esp_log_level_set("nvs", ESP_LOG_ERROR);
|
|
esp_log_level_set("wifi", ESP_LOG_ERROR);
|
|
*/
|
|
#endif
|
|
|
|
if(preferences->getBool(preference_mqtt_log_enabled))
|
|
{
|
|
esp_log_level_set("mqtt", ESP_LOG_NONE);
|
|
}
|
|
|
|
}
|
|
|
|
uint8_t checkPartition()
|
|
{
|
|
const esp_partition_t* running_partition = esp_ota_get_running_partition();
|
|
Log->print("Partition size: ");
|
|
Log->println(running_partition->size);
|
|
Log->print("Partition subtype: ");
|
|
Log->println(running_partition->subtype);
|
|
|
|
|
|
#if !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32P4)
|
|
if(running_partition->size == 1966080)
|
|
{
|
|
return 0; //OLD PARTITION TABLE
|
|
}
|
|
#endif
|
|
|
|
if(running_partition->subtype == ESP_PARTITION_SUBTYPE_APP_OTA_0)
|
|
{
|
|
return 1; //NEW PARTITION TABLE, RUNNING MAIN APP
|
|
}
|
|
else
|
|
{
|
|
return 2; //NEW PARTITION TABLE, RUNNING UPDATER APP
|
|
}
|
|
}
|
|
|
|
void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
|
|
Serial.printf("Listing directory: %s\r\n", dirname);
|
|
|
|
File root = fs.open(dirname);
|
|
if (!root) {
|
|
Serial.println("- failed to open directory");
|
|
return;
|
|
}
|
|
if (!root.isDirectory()) {
|
|
Serial.println(" - not a directory");
|
|
return;
|
|
}
|
|
|
|
File file = root.openNextFile();
|
|
while (file) {
|
|
if (file.isDirectory()) {
|
|
Serial.print(" DIR : ");
|
|
Serial.println(file.name());
|
|
if (levels) {
|
|
listDir(fs, file.path(), levels - 1);
|
|
}
|
|
} else {
|
|
Serial.print(" FILE: ");
|
|
Serial.print(file.name());
|
|
Serial.print("\tSIZE: ");
|
|
Serial.println(file.size());
|
|
}
|
|
|
|
if (file.size() > (int)(SPIFFS.totalBytes() * 0.4))
|
|
{
|
|
SPIFFS.remove((String)"/" + file.name());
|
|
}
|
|
|
|
file = root.openNextFile();
|
|
}
|
|
}
|
|
|
|
void cbSyncTime(struct timeval *tv) {
|
|
Log->println("NTP time synced");
|
|
timeSynced = true;
|
|
}
|
|
|
|
#ifndef NUKI_HUB_UPDATER
|
|
void startWebServer()
|
|
{
|
|
bool failed = true;
|
|
|
|
if (!nuki_hub_https_server_enabled) {
|
|
Log->println("Not running on PSRAM enabled device");
|
|
}
|
|
else
|
|
{
|
|
if (!SPIFFS.begin(true)) {
|
|
Log->println("SPIFFS Mount Failed");
|
|
}
|
|
else
|
|
{
|
|
File file = SPIFFS.open("/http_ssl.crt");
|
|
if (!file || file.isDirectory()) {
|
|
Log->println("http_ssl.crt not found");
|
|
}
|
|
else
|
|
{
|
|
Log->println("Reading http_ssl.crt");
|
|
size_t filesize = file.size();
|
|
char cert[filesize + 1];
|
|
|
|
file.read((uint8_t *)cert, sizeof(cert));
|
|
file.close();
|
|
cert[filesize] = '\0';
|
|
|
|
File file2 = SPIFFS.open("/http_ssl.key");
|
|
if (!file2 || file2.isDirectory()) {
|
|
Log->println("http_ssl.key not found");
|
|
}
|
|
else
|
|
{
|
|
Log->println("Reading http_ssl.key");
|
|
size_t filesize2 = file2.size();
|
|
char key[filesize2 + 1];
|
|
|
|
file2.read((uint8_t *)key, sizeof(key));
|
|
file2.close();
|
|
key[filesize2] = '\0';
|
|
|
|
psychicServerRedirect = new PsychicHttpServer();
|
|
psychicServerRedirect->config.ctrl_port = 20424;
|
|
psychicServerRedirect->onNotFound([](PsychicRequest* request, PsychicResponse* response) {
|
|
String url = "https://" + request->host() + request->url();
|
|
if (preferences->getString(preference_https_fqdn, "") != "")
|
|
{
|
|
url = "https://" + preferences->getString(preference_https_fqdn) + request->url();
|
|
}
|
|
|
|
response->setCode(301);
|
|
response->addHeader("Cache-Control", "no-cache");
|
|
return response->redirect(url.c_str());
|
|
});
|
|
psychicServerRedirect->begin();
|
|
psychicSSLServer = new PsychicHttpsServer;
|
|
psychicSSLServer->ssl_config.httpd.max_open_sockets = 8;
|
|
psychicSSLServer->setCertificate(cert, key);
|
|
psychicSSLServer->config.stack_size = HTTPD_TASK_SIZE;
|
|
webCfgServerSSL = new WebCfgServer(nuki, nukiOpener, network, gpio, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicSSLServer, importExport);
|
|
webCfgServerSSL->initialize();
|
|
psychicSSLServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) {
|
|
return response->redirect("/");
|
|
});
|
|
psychicSSLServer->begin();
|
|
webSSLStarted = true;
|
|
failed = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (failed)
|
|
{
|
|
psychicServer = new PsychicHttpServer;
|
|
psychicServer->config.stack_size = HTTPD_TASK_SIZE;
|
|
webCfgServer = new WebCfgServer(nuki, nukiOpener, network, gpio, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicServer, importExport);
|
|
webCfgServer->initialize();
|
|
psychicServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) {
|
|
return response->redirect("/");
|
|
});
|
|
psychicServer->begin();
|
|
webStarted = true;
|
|
}
|
|
}
|
|
|
|
void startNuki(bool lock)
|
|
{
|
|
if (lock)
|
|
{
|
|
nukiOfficial = new NukiOfficial(preferences);
|
|
networkLock = new NukiNetworkLock(network, nukiOfficial, preferences, CharBuffer::get(), buffer_size);
|
|
|
|
if(!disableNetwork)
|
|
{
|
|
networkLock->initialize();
|
|
}
|
|
|
|
lockStarted = true;
|
|
}
|
|
else
|
|
{
|
|
networkOpener = new NukiNetworkOpener(network, preferences, CharBuffer::get(), buffer_size);
|
|
|
|
if(!disableNetwork)
|
|
{
|
|
networkOpener->initialize();
|
|
}
|
|
|
|
openerStarted = true;
|
|
}
|
|
}
|
|
|
|
void restartServices(bool reconnect)
|
|
{
|
|
bleDone = false;
|
|
lockEnabled = preferences->getBool(preference_lock_enabled);
|
|
openerEnabled = preferences->getBool(preference_opener_enabled);
|
|
importExport->readSettings();
|
|
network->readSettings();
|
|
gpio->setPins();
|
|
|
|
if (reconnect)
|
|
{
|
|
network->reconnect(true);
|
|
}
|
|
|
|
if(webSSLStarted)
|
|
{
|
|
Log->println("Reset Psychic SSL server");
|
|
psychicSSLServer->reset();
|
|
Log->println("Reset Psychic SSL server done");
|
|
Log->println("Deleting Psychic SSL server");
|
|
delete psychicSSLServer;
|
|
psychicSSLServer = nullptr;
|
|
Log->println("Deleting Psychic SSL server done");
|
|
}
|
|
|
|
if(webStarted)
|
|
{
|
|
Log->println("Reset Psychic server");
|
|
psychicServer->reset();
|
|
Log->println("Reset Psychic server done");
|
|
Log->println("Deleting Psychic server");
|
|
delete psychicServer;
|
|
psychicServer = nullptr;
|
|
Log->println("Deleting Psychic server done");
|
|
}
|
|
|
|
if(webStarted || webSSLStarted)
|
|
{
|
|
Log->println("Deleting webCfgServer");
|
|
delete webCfgServer;
|
|
webCfgServer = nullptr;
|
|
Log->println("Deleting webCfgServer done");
|
|
}
|
|
|
|
if(lockStarted)
|
|
{
|
|
Log->println("Deleting nuki");
|
|
delete nuki;
|
|
nuki = nullptr;
|
|
if (reconnect)
|
|
{
|
|
lockStarted = false;
|
|
delete networkLock;
|
|
networkLock = nullptr;
|
|
delete nukiOfficial;
|
|
nukiOfficial = nullptr;
|
|
}
|
|
Log->println("Deleting nuki done");
|
|
}
|
|
|
|
if(openerStarted)
|
|
{
|
|
Log->println("Deleting nukiOpener");
|
|
delete nukiOpener;
|
|
nukiOpener = nullptr;
|
|
if (reconnect)
|
|
{
|
|
openerStarted = false;
|
|
delete networkOpener;
|
|
networkOpener = nullptr;
|
|
}
|
|
Log->println("Deleting nukiOpener done");
|
|
}
|
|
|
|
if (bleScannerStarted)
|
|
{
|
|
bleScannerStarted = false;
|
|
Log->println("Destroying scanner from main");
|
|
delete bleScanner;
|
|
Log->println("Scanner deleted");
|
|
bleScanner = nullptr;
|
|
Log->println("Scanner nulled from main");
|
|
}
|
|
|
|
if (BLEDevice::isInitialized()) {
|
|
Log->println("Deinit BLE device");
|
|
BLEDevice::deinit(false);
|
|
Log->println("Deinit BLE device done");
|
|
}
|
|
|
|
if (esp_task_wdt_status(NULL) == ESP_OK) {
|
|
esp_task_wdt_reset();
|
|
}
|
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
|
|
|
if(lockEnabled || openerEnabled)
|
|
{
|
|
Log->println("Restarting BLE Scanner");
|
|
bleScanner = new BleScanner::Scanner();
|
|
bleScanner->initialize("NukiHub", true, 40, 40);
|
|
bleScanner->setScanDuration(0);
|
|
bleScannerStarted = true;
|
|
Log->println("Restarting BLE Scanner done");
|
|
}
|
|
|
|
if(lockEnabled)
|
|
{
|
|
Log->println("Restarting Nuki lock");
|
|
|
|
if (reconnect)
|
|
{
|
|
startNuki(true);
|
|
}
|
|
|
|
nuki = new NukiWrapper("NukiHub", deviceIdLock, bleScanner, networkLock, nukiOfficial, gpio, preferences, CharBuffer::get(), buffer_size);
|
|
nuki->initialize();
|
|
bleScanner->whitelist(nuki->getBleAddress());
|
|
Log->println("Restarting Nuki lock done");
|
|
}
|
|
|
|
if(openerEnabled)
|
|
{
|
|
Log->println("Restarting Nuki opener");
|
|
|
|
if (reconnect)
|
|
{
|
|
startNuki(false);
|
|
}
|
|
|
|
nukiOpener = new NukiOpenerWrapper("NukiHub", deviceIdOpener, bleScanner, networkOpener, gpio, preferences, CharBuffer::get(), buffer_size);
|
|
nukiOpener->initialize();
|
|
bleScanner->whitelist(nukiOpener->getBleAddress());
|
|
Log->println("Restarting Nuki opener done");
|
|
}
|
|
|
|
|
|
if (esp_task_wdt_status(NULL) == ESP_OK) {
|
|
esp_task_wdt_reset();
|
|
}
|
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
|
bleDone = true;
|
|
|
|
if(webStarted || webSSLStarted)
|
|
{
|
|
Log->println("Restarting web server");
|
|
startWebServer();
|
|
Log->println("Restarting web server done");
|
|
}
|
|
else if(!doOta && !disableNetwork && (forceEnableWebServer || preferences->getBool(preference_webserver_enabled, true) || preferences->getBool(preference_webserial_enabled, false)))
|
|
{
|
|
if(forceEnableWebServer || preferences->getBool(preference_webserver_enabled, true))
|
|
{
|
|
Log->println("Starting web server");
|
|
startWebServer();
|
|
Log->println("Starting web server done");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void networkTask(void *pvParameters)
|
|
{
|
|
int64_t networkLoopTs = 0;
|
|
bool reroute = true;
|
|
if(preferences->getBool(preference_show_secrets, false))
|
|
{
|
|
preferences->putBool(preference_show_secrets, false);
|
|
}
|
|
while(true)
|
|
{
|
|
int64_t ts = espMillis();
|
|
if(ts > 120000 && ts < 125000)
|
|
{
|
|
if(bootloopCounter > 0)
|
|
{
|
|
bootloopCounter = (int8_t)0;
|
|
Log->println("Bootloop counter reset");
|
|
}
|
|
}
|
|
|
|
#ifndef NUKI_HUB_UPDATER
|
|
if(serialReader != nullptr)
|
|
{
|
|
serialReader->update();
|
|
}
|
|
#endif
|
|
network->update();
|
|
if (esp_task_wdt_status(NULL) == ESP_OK) {
|
|
esp_task_wdt_reset();
|
|
}
|
|
vTaskDelay(50 / portTICK_PERIOD_MS);
|
|
bool connected = network->isConnected();
|
|
|
|
if(connected && reroute)
|
|
{
|
|
if(preferences->getBool(preference_update_time, false))
|
|
{
|
|
esp_netif_sntp_start();
|
|
}
|
|
|
|
/* MDNS currently disabled for causing issues (9.10 / 2025-04-01)
|
|
if(webSSLStarted) {
|
|
if (MDNS.begin(preferences->getString(preference_hostname, "nukihub").c_str())) {
|
|
MDNS.addService("http", "tcp", 443);
|
|
}
|
|
}
|
|
else if(webStarted) {
|
|
if (MDNS.begin(preferences->getString(preference_hostname, "nukihub").c_str())) {
|
|
MDNS.addService("http", "tcp", 80);
|
|
}
|
|
}
|
|
*/
|
|
|
|
reroute = false;
|
|
setReroute();
|
|
}
|
|
|
|
#ifndef NUKI_HUB_UPDATER
|
|
wifiConnected = network->wifiConnected();
|
|
int restartServ = network->getRestartServices();
|
|
|
|
if (restartServ == 1)
|
|
{
|
|
restartServices(false);
|
|
}
|
|
else if (restartServ == 2)
|
|
{
|
|
restartServices(true);
|
|
}
|
|
else
|
|
{
|
|
if(connected && lockStarted)
|
|
{
|
|
rebootLock = networkLock->update();
|
|
if (esp_task_wdt_status(NULL) == ESP_OK) {
|
|
esp_task_wdt_reset();
|
|
}
|
|
vTaskDelay(50 / portTICK_PERIOD_MS);
|
|
}
|
|
|
|
if(connected && openerStarted)
|
|
{
|
|
networkOpener->update();
|
|
if (esp_task_wdt_status(NULL) == ESP_OK) {
|
|
esp_task_wdt_reset();
|
|
}
|
|
vTaskDelay(50 / portTICK_PERIOD_MS);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if(espMillis() - networkLoopTs > 120000)
|
|
{
|
|
Log->println("networkTask is running");
|
|
networkLoopTs = espMillis();
|
|
}
|
|
|
|
if(espMillis() > restartTs)
|
|
{
|
|
partitionType = checkPartition();
|
|
|
|
if(partitionType!=1)
|
|
{
|
|
esp_ota_set_boot_partition(esp_ota_get_next_update_partition(NULL));
|
|
}
|
|
|
|
restartEsp(RestartReason::RestartTimer);
|
|
}
|
|
|
|
if (esp_task_wdt_status(NULL) == ESP_OK) {
|
|
esp_task_wdt_reset();
|
|
}
|
|
vTaskDelay(50 / portTICK_PERIOD_MS);
|
|
}
|
|
}
|
|
|
|
#ifndef NUKI_HUB_UPDATER
|
|
#ifdef CONFIG_HEAP_TASK_TRACKING
|
|
static void print_all_tasks_info(void)
|
|
{
|
|
heap_all_tasks_stat_t tasks_stat;
|
|
/* call API to dynamically allocate the memory necessary to store the
|
|
* information collected while calling heap_caps_get_all_task_stat */
|
|
const esp_err_t ret_val = heap_caps_alloc_all_task_stat_arrays(&tasks_stat);
|
|
assert(ret_val == ESP_OK);
|
|
|
|
/* collect the information */
|
|
heap_caps_get_all_task_stat(&tasks_stat);
|
|
|
|
/* process the information retrieved */
|
|
Log->printf("\n--------------------------------------------------------------------------------\n");
|
|
Log->printf("PRINTING ALL TASKS INFO\n");
|
|
Log->printf("--------------------------------------------------------------------------------\n");
|
|
for (size_t task_idx = 0; task_idx < tasks_stat.task_count; task_idx++) {
|
|
task_stat_t task_stat = tasks_stat.stat_arr[task_idx];
|
|
Log->printf("%s: %s: Peak Usage %" PRIu16 ", Current Usage %" PRIu16 "\n", task_stat.name,
|
|
task_stat.is_alive ? "ALIVE " : "DELETED",
|
|
task_stat.overall_peak_usage,
|
|
task_stat.overall_current_usage);
|
|
|
|
for (size_t heap_idx = 0; heap_idx < task_stat.heap_count; heap_idx++) {
|
|
heap_stat_t heap_stat = task_stat.heap_stat[heap_idx];
|
|
Log->printf(" %s: Caps: %" PRIu32 ". Size %" PRIu16 ", Current Usage %" PRIu16 ", Peak Usage %" PRIu16 ", alloc count %" PRIu16 "\n", heap_stat.name,
|
|
heap_stat.caps,
|
|
heap_stat.size,
|
|
heap_stat.current_usage,
|
|
heap_stat.peak_usage,
|
|
heap_stat.alloc_count);
|
|
|
|
for (size_t alloc_idx = 0; alloc_idx < heap_stat.alloc_count; alloc_idx++) {
|
|
heap_task_block_t alloc_stat = heap_stat.alloc_stat[alloc_idx];
|
|
Log->printf(" %p: Size: %" PRIu32 "\n", alloc_stat.address, alloc_stat.size);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* delete the memory dynamically allocated while calling heap_caps_alloc_all_task_stat_arrays */
|
|
heap_caps_free_all_task_stat_arrays(&tasks_stat);
|
|
}
|
|
#endif
|
|
void nukiTask(void *pvParameters)
|
|
{
|
|
esp_task_wdt_add(NULL);
|
|
|
|
if (preferences->getBool(preference_mqtt_ssl_enabled, false))
|
|
{
|
|
#if defined(CONFIG_SOC_SPIRAM_SUPPORTED) && defined(CONFIG_SPIRAM)
|
|
if (esp_psram_get_size() <= 0)
|
|
{
|
|
Log->println("Waiting 20 seconds to start BLE because of MQTT SSL");
|
|
if (esp_task_wdt_status(NULL) == ESP_OK) {
|
|
esp_task_wdt_reset();
|
|
}
|
|
vTaskDelay(20000 / portTICK_PERIOD_MS);
|
|
}
|
|
#else
|
|
Log->println("Waiting 20 seconds to start BLE because of MQTT SSL");
|
|
if (esp_task_wdt_status(NULL) == ESP_OK) {
|
|
esp_task_wdt_reset();
|
|
}
|
|
vTaskDelay(20000 / portTICK_PERIOD_MS);
|
|
#endif
|
|
}
|
|
int64_t nukiLoopTs = 0;
|
|
bool whiteListed = false;
|
|
while(true)
|
|
{
|
|
if((disableNetwork || wifiConnected) && bleDone)
|
|
{
|
|
if(bleScannerStarted)
|
|
{
|
|
bleScanner->update();
|
|
if (esp_task_wdt_status(NULL) == ESP_OK) {
|
|
esp_task_wdt_reset();
|
|
}
|
|
vTaskDelay(20 / portTICK_PERIOD_MS);
|
|
}
|
|
|
|
bool needsPairing = (lockStarted && !nuki->isPaired()) || (openerStarted && !nukiOpener->isPaired());
|
|
|
|
if (needsPairing)
|
|
{
|
|
if (esp_task_wdt_status(NULL) == ESP_OK) {
|
|
esp_task_wdt_reset();
|
|
}
|
|
vTaskDelay(2500 / portTICK_PERIOD_MS);
|
|
}
|
|
else if (!whiteListed)
|
|
{
|
|
whiteListed = true;
|
|
if(lockEnabled)
|
|
{
|
|
bleScanner->whitelist(nuki->getBleAddress());
|
|
}
|
|
if(openerEnabled)
|
|
{
|
|
bleScanner->whitelist(nukiOpener->getBleAddress());
|
|
}
|
|
}
|
|
|
|
if(lockStarted)
|
|
{
|
|
if (nuki->restartController() > 0)
|
|
{
|
|
if (lockRestartControllerCount > 3)
|
|
{
|
|
if (nuki->restartController() == 1)
|
|
{
|
|
restartEsp(RestartReason::BLEError);
|
|
}
|
|
else if (nuki->restartController() == 2)
|
|
{
|
|
restartEsp(RestartReason::BLEBeaconWatchdog);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lockRestartControllerCount += 1;
|
|
restartServices(false);
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (lockRestartControllerCount > 0 && nuki->hasConnected())
|
|
{
|
|
lockRestartControllerCount = 0;
|
|
}
|
|
|
|
nuki->update(rebootLock);
|
|
rebootLock = false;
|
|
}
|
|
}
|
|
if(openerStarted)
|
|
{
|
|
if (nukiOpener->restartController() > 0)
|
|
{
|
|
if (openerRestartControllerCount > 3)
|
|
{
|
|
if (nukiOpener->restartController() == 1)
|
|
{
|
|
restartEsp(RestartReason::BLEError);
|
|
}
|
|
else if (nukiOpener->restartController() == 2)
|
|
{
|
|
restartEsp(RestartReason::BLEBeaconWatchdog);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
openerRestartControllerCount += 1;
|
|
restartServices(false);
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (openerRestartControllerCount > 0 && nukiOpener->hasConnected())
|
|
{
|
|
openerRestartControllerCount = 0;
|
|
}
|
|
|
|
nukiOpener->update();
|
|
}
|
|
}
|
|
}
|
|
|
|
if(espMillis() - nukiLoopTs > 120000)
|
|
{
|
|
Log->println("nukiTask is running");
|
|
nukiLoopTs = espMillis();
|
|
}
|
|
if (esp_task_wdt_status(NULL) == ESP_OK) {
|
|
esp_task_wdt_reset();
|
|
}
|
|
vTaskDelay(50 / portTICK_PERIOD_MS);
|
|
}
|
|
}
|
|
|
|
void bootloopDetection()
|
|
{
|
|
uint64_t cmp = IS_VALID_DETECT;
|
|
bool bootloopIsValid = (bootloopValidDetect == cmp);
|
|
Log->println(bootloopIsValid);
|
|
|
|
if(!bootloopIsValid)
|
|
{
|
|
bootloopCounter = (int8_t)0;
|
|
bootloopValidDetect = IS_VALID_DETECT;
|
|
return;
|
|
}
|
|
|
|
if(esp_reset_reason() == esp_reset_reason_t::ESP_RST_PANIC ||
|
|
esp_reset_reason() == esp_reset_reason_t::ESP_RST_INT_WDT ||
|
|
esp_reset_reason() == esp_reset_reason_t::ESP_RST_TASK_WDT ||
|
|
esp_reset_reason() == esp_reset_reason_t::ESP_RST_WDT)
|
|
{
|
|
bootloopCounter++;
|
|
Log->print("Bootloop counter incremented: ");
|
|
Log->println(bootloopCounter);
|
|
|
|
if(bootloopCounter == 10)
|
|
{
|
|
Log->print("Bootloop detected.");
|
|
|
|
preferences->putInt(preference_buffer_size, CHAR_BUFFER_SIZE);
|
|
preferences->putInt(preference_task_size_network, NETWORK_TASK_SIZE);
|
|
preferences->putInt(preference_task_size_nuki, NUKI_TASK_SIZE);
|
|
preferences->putInt(preference_authlog_max_entries, MAX_AUTHLOG);
|
|
preferences->putInt(preference_keypad_max_entries, MAX_KEYPAD);
|
|
preferences->putInt(preference_timecontrol_max_entries, MAX_TIMECONTROL);
|
|
preferences->putInt(preference_auth_max_entries, MAX_AUTH);
|
|
bootloopCounter = 0;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
|
|
{
|
|
if (lastHTTPeventId != int(evt->event_id))
|
|
{
|
|
Log->println("");
|
|
switch (evt->event_id)
|
|
{
|
|
case HTTP_EVENT_ERROR:
|
|
Log->println("HTTP_EVENT_ERROR");
|
|
break;
|
|
case HTTP_EVENT_ON_CONNECTED:
|
|
Log->print("HTTP_EVENT_ON_CONNECTED");
|
|
break;
|
|
case HTTP_EVENT_HEADER_SENT:
|
|
Log->print("HTTP_EVENT_HEADER_SENT");
|
|
break;
|
|
case HTTP_EVENT_ON_HEADER:
|
|
Log->print("HTTP_EVENT_ON_HEADER");
|
|
break;
|
|
case HTTP_EVENT_ON_DATA:
|
|
Log->print("HTTP_EVENT_ON_DATA");
|
|
break;
|
|
case HTTP_EVENT_ON_FINISH:
|
|
Log->println("HTTP_EVENT_ON_FINISH");
|
|
break;
|
|
case HTTP_EVENT_DISCONNECTED:
|
|
Log->println("HTTP_EVENT_DISCONNECTED");
|
|
break;
|
|
case HTTP_EVENT_REDIRECT:
|
|
Log->print("HTTP_EVENT_REDIRECT");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log->print(".");
|
|
}
|
|
lastHTTPeventId = int(evt->event_id);
|
|
wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT();
|
|
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
|
|
wdt_hal_feed(&rtc_wdt_ctx);
|
|
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
void otaTask(void *pvParameter)
|
|
{
|
|
esp_task_wdt_add(NULL);
|
|
|
|
partitionType = checkPartition();
|
|
String updateUrl;
|
|
|
|
if(partitionType==1)
|
|
{
|
|
updateUrl = preferences->getString(preference_ota_updater_url);
|
|
preferences->putString(preference_ota_updater_url, "");
|
|
}
|
|
else
|
|
{
|
|
updateUrl = preferences->getString(preference_ota_main_url);
|
|
preferences->putString(preference_ota_main_url, "");
|
|
}
|
|
|
|
while(!network->isConnected())
|
|
{
|
|
Log->println("OTA waiting for network");
|
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
|
}
|
|
|
|
Log->println("Starting OTA task");
|
|
esp_http_client_config_t config =
|
|
{
|
|
.url = updateUrl.c_str(),
|
|
.event_handler = _http_event_handler,
|
|
.crt_bundle_attach = esp_crt_bundle_attach,
|
|
.keep_alive_enable = true,
|
|
};
|
|
|
|
esp_https_ota_config_t ota_config =
|
|
{
|
|
.http_config = &config,
|
|
};
|
|
Log->print("Attempting to download update from ");
|
|
Log->println(config.url);
|
|
|
|
int retryMax = 3;
|
|
int retryCount = 0;
|
|
|
|
while (retryCount <= retryMax)
|
|
{
|
|
esp_err_t ret = esp_https_ota(&ota_config);
|
|
if (ret == ESP_OK)
|
|
{
|
|
Log->println("OTA Succeeded, Rebooting...");
|
|
esp_ota_set_boot_partition(esp_ota_get_next_update_partition(NULL));
|
|
restartEsp(RestartReason::OTACompleted);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
Log->println("Firmware upgrade failed, retrying in 5 seconds");
|
|
retryCount++;
|
|
if (esp_task_wdt_status(NULL) == ESP_OK) {
|
|
esp_task_wdt_reset();
|
|
}
|
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
|
continue;
|
|
}
|
|
while (1)
|
|
{
|
|
if (esp_task_wdt_status(NULL) == ESP_OK) {
|
|
esp_task_wdt_reset();
|
|
}
|
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
|
}
|
|
}
|
|
Log->println("Firmware upgrade failed, restarting");
|
|
esp_ota_set_boot_partition(esp_ota_get_next_update_partition(NULL));
|
|
restartEsp(RestartReason::OTAAborted);
|
|
}
|
|
|
|
void setupTasks(bool ota)
|
|
{
|
|
// configMAX_PRIORITIES is 25
|
|
esp_task_wdt_config_t twdt_config =
|
|
{
|
|
.timeout_ms = 300000,
|
|
.idle_core_mask = (1 << CONFIG_FREERTOS_NUMBER_OF_CORES) - 1,
|
|
.trigger_panic = true,
|
|
};
|
|
esp_task_wdt_reconfigure(&twdt_config);
|
|
|
|
esp_chip_info_t info;
|
|
esp_chip_info(&info);
|
|
uint8_t espCores = info.cores;
|
|
Log->print("Cores: ");
|
|
Log->println(espCores);
|
|
|
|
if(ota)
|
|
{
|
|
xTaskCreatePinnedToCore(otaTask, "ota", 8192, NULL, 2, &otaTaskHandle, (espCores > 1) ? 1 : 0);
|
|
}
|
|
else
|
|
{
|
|
if(!disableNetwork)
|
|
{
|
|
xTaskCreatePinnedToCore(networkTask, "ntw", preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE), NULL, 3, &networkTaskHandle, (espCores > 1) ? 1 : 0);
|
|
}
|
|
#ifndef NUKI_HUB_UPDATER
|
|
if(!network->isApOpen() && (lockEnabled || openerEnabled))
|
|
{
|
|
xTaskCreatePinnedToCore(nukiTask, "nuki", preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE), NULL, 2, &nukiTaskHandle, 0);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void logCoreDump()
|
|
{
|
|
coredumpPrinted = false;
|
|
if (esp_task_wdt_status(NULL) == ESP_OK) {
|
|
esp_task_wdt_reset();
|
|
}
|
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
|
Log->println("Printing coredump and saving to coredump.hex on SPIFFS");
|
|
size_t size = 0;
|
|
size_t address = 0;
|
|
if (esp_core_dump_image_get(&address, &size) == ESP_OK)
|
|
{
|
|
const esp_partition_t *pt = NULL;
|
|
pt = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, "coredump");
|
|
|
|
if (pt != NULL)
|
|
{
|
|
File file;
|
|
uint8_t bf[256];
|
|
char str_dst[640];
|
|
int16_t toRead;
|
|
|
|
if (!SPIFFS.begin(true))
|
|
{
|
|
Log->println("SPIFFS Mount Failed");
|
|
}
|
|
else
|
|
{
|
|
file = SPIFFS.open("/coredump.hex", FILE_WRITE);
|
|
if (!file) {
|
|
Log->println("Failed to open /coredump.hex for writing");
|
|
}
|
|
else
|
|
{
|
|
file.printf("%s\r\n", NUKI_HUB_HW);
|
|
file.printf("%s\r\n", NUKI_HUB_BUILD);
|
|
}
|
|
}
|
|
|
|
Serial.printf("%s\r\n", NUKI_HUB_HW);
|
|
Serial.printf("%s\r\n", NUKI_HUB_BUILD);
|
|
|
|
for (int16_t i = 0; i < (size/256)+1; i++)
|
|
{
|
|
strcpy(str_dst, "");
|
|
toRead = (size - i*256) > 256 ? 256 : (size - i*256);
|
|
|
|
esp_err_t er = esp_partition_read(pt, i*256, bf, toRead);
|
|
if (er != ESP_OK)
|
|
{
|
|
Serial.printf("FAIL [%x]", er);
|
|
break;
|
|
}
|
|
|
|
for (int16_t j = 0; j < 256; j++)
|
|
{
|
|
char str_tmp[2];
|
|
if (bf[j] <= 0x0F)
|
|
{
|
|
sprintf(str_tmp, "0%x", bf[j]);
|
|
}
|
|
else
|
|
{
|
|
sprintf(str_tmp, "%x", bf[j]);
|
|
}
|
|
strcat(str_dst, str_tmp);
|
|
}
|
|
Serial.printf("%s", str_dst);
|
|
|
|
if (file) {
|
|
file.printf("%s", str_dst);
|
|
}
|
|
}
|
|
|
|
Serial.println("");
|
|
|
|
if (file) {
|
|
file.println("");
|
|
file.close();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Serial.println("Partition NULL");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Serial.println("esp_core_dump_image_get() FAIL");
|
|
}
|
|
coredumpPrinted = true;
|
|
}
|
|
|
|
void setup()
|
|
{
|
|
#if defined(CONFIG_SOC_SPIRAM_SUPPORTED) && defined(CONFIG_SPIRAM)
|
|
#ifndef FORCE_NUKI_HUB_HTTPS_SERVER
|
|
if(esp_psram_get_size() <= 0)
|
|
{
|
|
nuki_hub_https_server_enabled = false;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
//Set Log level to error for all TAGS
|
|
esp_log_level_set("*", ESP_LOG_ERROR);
|
|
//Set Log level to none for mqtt TAG
|
|
esp_log_level_set("mqtt", ESP_LOG_NONE);
|
|
//Start Serial and setup Log class
|
|
Serial.begin(115200);
|
|
Log = &Serial;
|
|
|
|
#if !defined(NUKI_HUB_UPDATER) && !defined(CONFIG_IDF_TARGET_ESP32C5)
|
|
stdout = funopen(NULL, NULL, &write_fn, NULL, NULL);
|
|
static char linebuf[1024];
|
|
setvbuf(stdout, linebuf, _IOLBF, sizeof(linebuf));
|
|
esp_rom_install_channel_putc(1, &ets_putc_handler);
|
|
//ets_install_putc1(&ets_putc_handler);
|
|
#endif
|
|
|
|
preferences = new Preferences();
|
|
preferences->begin("nukihub", false);
|
|
initPreferences(preferences);
|
|
initializeRestartReason();
|
|
|
|
if(esp_reset_reason() == esp_reset_reason_t::ESP_RST_PANIC ||
|
|
esp_reset_reason() == esp_reset_reason_t::ESP_RST_INT_WDT ||
|
|
esp_reset_reason() == esp_reset_reason_t::ESP_RST_TASK_WDT)
|
|
//|| esp_reset_reason() == esp_reset_reason_t::ESP_RST_WDT)
|
|
{
|
|
logCoreDump();
|
|
}
|
|
|
|
if (SPIFFS.begin(true))
|
|
{
|
|
listDir(SPIFFS, "/", 1);
|
|
}
|
|
|
|
partitionType = checkPartition();
|
|
|
|
//default disableNetwork RTC_ATTR to false on power-on
|
|
if(espRunning != 1)
|
|
{
|
|
espRunning = 1;
|
|
forceEnableWebServer = false;
|
|
disableNetwork = false;
|
|
wifiFallback = false;
|
|
ethCriticalFailure = false;
|
|
}
|
|
|
|
//determine if an OTA update was requested
|
|
if((partitionType==1 && preferences->getString(preference_ota_updater_url, "").length() > 0) || (partitionType==2 && preferences->getString(preference_ota_main_url, "").length() > 0))
|
|
{
|
|
doOta = true;
|
|
}
|
|
|
|
#ifdef NUKI_HUB_UPDATER
|
|
Log->print("Nuki Hub OTA version ");
|
|
Log->println(NUKI_HUB_VERSION);
|
|
Log->print("Nuki Hub OTA build ");
|
|
Log->println();
|
|
|
|
if(preferences->getString(preference_updater_version, "") != NUKI_HUB_VERSION)
|
|
{
|
|
preferences->putString(preference_updater_version, NUKI_HUB_VERSION);
|
|
}
|
|
if(preferences->getString(preference_updater_build, "") != NUKI_HUB_BUILD)
|
|
{
|
|
preferences->putString(preference_updater_build, NUKI_HUB_BUILD);
|
|
}
|
|
if(preferences->getString(preference_updater_date, "") != NUKI_HUB_DATE)
|
|
{
|
|
preferences->putString(preference_updater_date, NUKI_HUB_DATE);
|
|
}
|
|
|
|
importExport = new ImportExport(preferences);
|
|
|
|
network = new NukiNetwork(preferences);
|
|
network->initialize();
|
|
|
|
if(!doOta)
|
|
{
|
|
bool failed = true;
|
|
|
|
if (!nuki_hub_https_server_enabled) {
|
|
Log->println("Not running on HTTPS server enabled device");
|
|
}
|
|
else
|
|
{
|
|
if (!SPIFFS.begin(true)) {
|
|
Log->println("SPIFFS Mount Failed");
|
|
}
|
|
else
|
|
{
|
|
File file = SPIFFS.open("/http_ssl.crt");
|
|
if (!file || file.isDirectory()) {
|
|
Log->println("http_ssl.crt not found");
|
|
}
|
|
else
|
|
{
|
|
Log->println("Reading http_ssl.crt");
|
|
size_t filesize = file.size();
|
|
char cert[filesize + 1];
|
|
|
|
file.read((uint8_t *)cert, sizeof(cert));
|
|
file.close();
|
|
cert[filesize] = '\0';
|
|
|
|
File file2 = SPIFFS.open("/http_ssl.key");
|
|
if (!file2 || file2.isDirectory()) {
|
|
Log->println("http_ssl.key not found");
|
|
}
|
|
else
|
|
{
|
|
Log->println("Reading http_ssl.key");
|
|
size_t filesize2 = file2.size();
|
|
char key[filesize2 + 1];
|
|
|
|
file2.read((uint8_t *)key, sizeof(key));
|
|
file2.close();
|
|
key[filesize2] = '\0';
|
|
|
|
psychicServer = new PsychicHttpServer();
|
|
psychicServer->config.ctrl_port = 20424;
|
|
psychicServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) {
|
|
String url = "https://" + request->host() + request->url();
|
|
if (preferences->getString(preference_https_fqdn, "") != "")
|
|
{
|
|
url = "https://" + preferences->getString(preference_https_fqdn) + request->url();
|
|
}
|
|
|
|
response->setCode(301);
|
|
response->addHeader("Cache-Control", "no-cache");
|
|
return response->redirect(url.c_str());
|
|
});
|
|
psychicServer->begin();
|
|
psychicSSLServer = new PsychicHttpsServer;
|
|
psychicSSLServer->ssl_config.httpd.max_open_sockets = 8;
|
|
psychicSSLServer->setCertificate(cert, key);
|
|
psychicSSLServer->config.stack_size = HTTPD_TASK_SIZE;
|
|
webCfgServerSSL = new WebCfgServer(network, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicSSLServer, importExport);
|
|
webCfgServerSSL->initialize();
|
|
psychicSSLServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) {
|
|
return response->redirect("/");
|
|
});
|
|
psychicSSLServer->begin();
|
|
webSSLStarted = true;
|
|
failed = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (failed)
|
|
{
|
|
psychicServer = new PsychicHttpServer;
|
|
psychicServer->config.stack_size = HTTPD_TASK_SIZE;
|
|
webCfgServer = new WebCfgServer(network, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicServer, importExport);
|
|
webCfgServer->initialize();
|
|
psychicServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) {
|
|
return response->redirect("/");
|
|
});
|
|
psychicServer->begin();
|
|
webStarted = true;
|
|
}
|
|
}
|
|
#else
|
|
if(preferences->getBool(preference_enable_bootloop_reset, false))
|
|
{
|
|
bootloopDetection();
|
|
}
|
|
|
|
Log->print("Nuki Hub version ");
|
|
Log->println(NUKI_HUB_VERSION);
|
|
Log->print("Nuki Hub build ");
|
|
Log->println(NUKI_HUB_BUILD);
|
|
|
|
Log->println(preferences->getString(preference_cred_user));
|
|
Log->println(preferences->getString(preference_cred_password));
|
|
|
|
uint32_t devIdOpener = preferences->getUInt(preference_device_id_opener);
|
|
|
|
deviceIdLock = new NukiDeviceId(preferences, preference_device_id_lock);
|
|
deviceIdOpener = new NukiDeviceId(preferences, preference_device_id_opener);
|
|
|
|
if(deviceIdLock->get() != 0 && devIdOpener == 0)
|
|
{
|
|
deviceIdOpener->assignId(deviceIdLock->get());
|
|
}
|
|
|
|
buffer_size = preferences->getInt(preference_buffer_size, CHAR_BUFFER_SIZE);
|
|
CharBuffer::initialize(buffer_size);
|
|
|
|
gpio = new Gpio(preferences);
|
|
String gpioDesc;
|
|
gpio->getConfigurationText(gpioDesc, gpio->pinConfiguration(), "\n\r");
|
|
Log->print(gpioDesc.c_str());
|
|
|
|
importExport = new ImportExport(preferences);
|
|
|
|
network = new NukiNetwork(preferences, gpio, CharBuffer::get(), buffer_size, importExport);
|
|
network->initialize();
|
|
|
|
lockEnabled = preferences->getBool(preference_lock_enabled);
|
|
openerEnabled = preferences->getBool(preference_opener_enabled);
|
|
|
|
if(network->isApOpen())
|
|
{
|
|
forceEnableWebServer = true;
|
|
doOta = false;
|
|
lockEnabled = false;
|
|
openerEnabled = false;
|
|
#ifndef NUKI_HUB_UPDATER
|
|
serialReader = new SerialReader(importExport, network);
|
|
#endif
|
|
}
|
|
|
|
if(lockEnabled || openerEnabled)
|
|
{
|
|
bleScanner = new BleScanner::Scanner();
|
|
// Scan interval and window according to Nuki recommendations:
|
|
// https://developer.nuki.io/t/bluetooth-specification-questions/1109/27
|
|
bleScanner->initialize("NukiHub", true, 40, 40);
|
|
bleScanner->setScanDuration(0);
|
|
bleScannerStarted = true;
|
|
}
|
|
|
|
Log->println(lockEnabled ? F("Nuki Lock enabled") : F("Nuki Lock disabled"));
|
|
if(lockEnabled)
|
|
{
|
|
startNuki(true);
|
|
|
|
nuki = new NukiWrapper("NukiHub", deviceIdLock, bleScanner, networkLock, nukiOfficial, gpio, preferences, CharBuffer::get(), buffer_size);
|
|
nuki->initialize();
|
|
}
|
|
|
|
Log->println(openerEnabled ? F("Nuki Opener enabled") : F("Nuki Opener disabled"));
|
|
if(openerEnabled)
|
|
{
|
|
startNuki(false);
|
|
|
|
nukiOpener = new NukiOpenerWrapper("NukiHub", deviceIdOpener, bleScanner, networkOpener, gpio, preferences, CharBuffer::get(), buffer_size);
|
|
nukiOpener->initialize();
|
|
}
|
|
|
|
bleDone = true;
|
|
|
|
if(!doOta && !disableNetwork && (forceEnableWebServer || preferences->getBool(preference_webserver_enabled, true) || preferences->getBool(preference_webserial_enabled, false)))
|
|
{
|
|
if(forceEnableWebServer || preferences->getBool(preference_webserver_enabled, true))
|
|
{
|
|
startWebServer();
|
|
}
|
|
/*
|
|
#ifdef DEBUG_NUKIHUB
|
|
else psychicServer->onNotFound([](PsychicRequest* request) { return request->redirect("/webserial"); });
|
|
|
|
if(preferences->getBool(preference_webserial_enabled, false))
|
|
{
|
|
WebSerial.setAuthentication(preferences->getString(preference_cred_user), preferences->getString(preference_cred_password));
|
|
WebSerial.begin(psychicServer);
|
|
WebSerial.setBuffer(1024);
|
|
}
|
|
#endif
|
|
*/
|
|
}
|
|
#endif
|
|
|
|
String timeserver = preferences->getString(preference_time_server, "pool.ntp.org");
|
|
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(timeserver.c_str());
|
|
config.start = false;
|
|
config.server_from_dhcp = true;
|
|
config.renew_servers_after_new_IP = true;
|
|
config.index_of_first_server = 1;
|
|
|
|
if (network->networkDeviceType() == NetworkDeviceType::WiFi)
|
|
{
|
|
config.ip_event_to_renew = IP_EVENT_STA_GOT_IP;
|
|
}
|
|
else
|
|
{
|
|
config.ip_event_to_renew = IP_EVENT_ETH_GOT_IP;
|
|
}
|
|
config.sync_cb = cbSyncTime;
|
|
esp_netif_sntp_init(&config);
|
|
|
|
if(doOta)
|
|
{
|
|
setupTasks(true);
|
|
}
|
|
else
|
|
{
|
|
setupTasks(false);
|
|
}
|
|
|
|
//print_all_tasks_info();
|
|
}
|
|
|
|
void loop()
|
|
{
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
void printBeforeSetupInfo()
|
|
{
|
|
}
|
|
|
|
void printAfterSetupInfo()
|
|
{
|
|
}
|