diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index d8074c9..0719b7c 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,8 +1,18 @@ set(headers + config.h + debugconsole.h + ota.h + taskmanager.h + wifi.h ) set(sources + config.cpp + debugconsole.cpp main.cpp + ota.cpp + taskmanager.cpp + wifi.cpp ) set(dependencies diff --git a/main/config.cpp b/main/config.cpp new file mode 100644 index 0000000..701a84f --- /dev/null +++ b/main/config.cpp @@ -0,0 +1,33 @@ +#include "config.h" + +#include "sdkconfig.h" + +// esp-idf includes +#include + +// 3rdparty lib includes +#include +#include +#include + +// local includes +#include "configmanager_priv.h" + +using namespace std::chrono_literals; + +namespace { +//constexpr const char * const TAG = "CONFIG"; +} // namespace + +std::string defaultHostname() +{ + if (const auto result = wifi_stack::get_default_mac_addr()) + return fmt::format("bobby_{:02x}{:02x}{:02x}", result->at(3), result->at(4), result->at(5)); + else + ESP_LOGE(TAG, "get_default_mac_addr() failed: %.*s", result.error().size(), result.error().data()); + return "bobby"; +} + +ConfigManager configs; + +INSTANTIATE_CONFIGMANAGER_TEMPLATES(ConfigContainer) diff --git a/main/config.h b/main/config.h new file mode 100644 index 0000000..1d132fa --- /dev/null +++ b/main/config.h @@ -0,0 +1,168 @@ +#pragma once + +#include "sdkconfig.h" + +// system includes +#include +#include +#include + +// esp-idf includes +#include + +// 3rdparty lib includes +#include +#include +#include +#include +#include +#include +#include +#include + +// local includes + +using namespace espconfig; + +std::string defaultHostname(); + +class WiFiConfig +{ +public: + WiFiConfig(const char *ssidNvsKey, const char *keyNvsKey, + const char *useStaticIpKey, const char *staticIpKey, const char *staticSubnetKey, const char *staticGatewayKey, + const char *useStaticDnsKey, const char *staticDns0Key, const char *staticDns1Key, const char *staticDns2Key) : + ssid {std::string{}, DoReset, StringMaxSize<32>, ssidNvsKey }, + key {std::string{}, DoReset, StringOr>, keyNvsKey }, + useStaticIp {false, DoReset, {}, useStaticIpKey }, + staticIp {wifi_stack::ip_address_t{}, DoReset, {}, staticIpKey }, + staticSubnet {wifi_stack::ip_address_t{}, DoReset, {}, staticSubnetKey }, + staticGateway{wifi_stack::ip_address_t{}, DoReset, {}, staticGatewayKey }, + useStaticDns {false, DoReset, {}, useStaticDnsKey }, + staticDns0 {wifi_stack::ip_address_t{}, DoReset, {}, staticDns0Key }, + staticDns1 {wifi_stack::ip_address_t{}, DoReset, {}, staticDns1Key }, + staticDns2 {wifi_stack::ip_address_t{}, DoReset, {}, staticDns2Key } + {} + + ConfigWrapper ssid; + ConfigWrapper key; + ConfigWrapper useStaticIp; + ConfigWrapper staticIp; + ConfigWrapper staticSubnet; + ConfigWrapper staticGateway; + ConfigWrapper useStaticDns; + ConfigWrapper staticDns0; + ConfigWrapper staticDns1; + ConfigWrapper staticDns2; +}; + +class ConfigContainer +{ + using mac_t = wifi_stack::mac_t; + +public: + // default allowReset constraints nvsName + ConfigWrapper> baseMacAddressOverride{std::nullopt, DoReset, {}, "baseMacAddrOver" }; + ConfigWrapper hostname {defaultHostname, DoReset, StringMinMaxSize<4, 32>, "hostname" }; + +#ifdef CONFIG_ETH_ENABLED + ConfigWrapper ethEnabled {true, DoReset, {}, "ethEnabled" }; + ConfigWrapper ethUseStaticIp {false, DoReset, {}, "ethUseStatIp" }; + ConfigWrapper ethStaticIp {ip_address_t{}, DoReset, {}, "ethStaticIp" }; + ConfigWrapper ethStaticSubnet {ip_address_t{}, DoReset, {}, "ethStaticSub" }; + ConfigWrapper ethStaticGateway {ip_address_t{}, DoReset, {}, "ethStaticGw" }; + ConfigWrapper ethUseStaticDns {false, DoReset, {}, "ethUseStatDns" }; + ConfigWrapper ethStaticDns0 {ip_address_t{}, DoReset, {}, "ethStaticDns0" }; + ConfigWrapper ethStaticDns1 {ip_address_t{}, DoReset, {}, "ethStaticDns1" }; + ConfigWrapper ethStaticDns2 {ip_address_t{}, DoReset, {}, "ethStaticDns2" }; +#endif + + ConfigWrapper wifiStaEnabled {true, DoReset, {}, "wifiStaEnabled" }; + std::array wifi_configs { + WiFiConfig {"wifi_ssid0", "wifi_key0", "wifi_usestatic0", "wifi_static_ip0", "wifi_stati_sub0", "wifi_stat_gate0", "wifi_usestadns0", "wifi_stat_dnsA0", "wifi_stat_dnsB0", "wifi_stat_dnsC0"}, + WiFiConfig {"wifi_ssid1", "wifi_key1", "wifi_usestatic1", "wifi_static_ip1", "wifi_stati_sub1", "wifi_stat_gate1", "wifi_usestadns1", "wifi_stat_dnsA1", "wifi_stat_dnsB1", "wifi_stat_dnsC1"}, + WiFiConfig {"wifi_ssid2", "wifi_key2", "wifi_usestatic2", "wifi_static_ip2", "wifi_stati_sub2", "wifi_stat_gate2", "wifi_usestadns2", "wifi_stat_dnsA2", "wifi_stat_dnsB2", "wifi_stat_dnsC2"}, + WiFiConfig {"wifi_ssid3", "wifi_key3", "wifi_usestatic3", "wifi_static_ip3", "wifi_stati_sub3", "wifi_stat_gate3", "wifi_usestadns3", "wifi_stat_dnsA3", "wifi_stat_dnsB3", "wifi_stat_dnsC3"}, + WiFiConfig {"wifi_ssid4", "wifi_key4", "wifi_usestatic4", "wifi_static_ip4", "wifi_stati_sub4", "wifi_stat_gate4", "wifi_usestadns4", "wifi_stat_dnsA4", "wifi_stat_dnsB4", "wifi_stat_dnsC4"}, + WiFiConfig {"wifi_ssid5", "wifi_key5", "wifi_usestatic5", "wifi_static_ip5", "wifi_stati_sub5", "wifi_stat_gate5", "wifi_usestadns5", "wifi_stat_dnsA5", "wifi_stat_dnsB5", "wifi_stat_dnsC5"}, + WiFiConfig {"wifi_ssid6", "wifi_key6", "wifi_usestatic6", "wifi_static_ip6", "wifi_stati_sub6", "wifi_stat_gate6", "wifi_usestadns6", "wifi_stat_dnsA6", "wifi_stat_dnsB6", "wifi_stat_dnsC6"}, + WiFiConfig {"wifi_ssid7", "wifi_key7", "wifi_usestatic7", "wifi_static_ip7", "wifi_stati_sub7", "wifi_stat_gate7", "wifi_usestadns7", "wifi_stat_dnsA7", "wifi_stat_dnsB7", "wifi_stat_dnsC7"}, + WiFiConfig {"wifi_ssid8", "wifi_key8", "wifi_usestatic8", "wifi_static_ip8", "wifi_stati_sub8", "wifi_stat_gate8", "wifi_usestadns8", "wifi_stat_dnsA8", "wifi_stat_dnsB8", "wifi_stat_dnsC8"}, + WiFiConfig {"wifi_ssid9", "wifi_key9", "wifi_usestatic9", "wifi_static_ip9", "wifi_stati_sub9", "wifi_stat_gate9", "wifi_usestadns9", "wifi_stat_dnsA9", "wifi_stat_dnsB9", "wifi_stat_dnsC9"} + }; + ConfigWrapper wifiStaMinRssi {-90, DoReset, {}, "wifiStaMinRssi" }; + + ConfigWrapper wifiApEnabled {true, DoReset, {}, "wifiApEnabled" }; + ConfigWrapper wifiApName {defaultHostname, DoReset, StringMinMaxSize<4, 32>, "wifiApName" }; + ConfigWrapper wifiApKey {"Passwort_123", DoReset, StringOr>, "wifiApKey" }; + ConfigWrapper wifiApIp{wifi_stack::ip_address_t{10, 0, 0, 1},DoReset, {}, "wifiApIp" }; + ConfigWrapper wifiApMask{wifi_stack::ip_address_t{255, 255, 255, 0},DoReset, {}, "wifiApMask" }; + ConfigWrapper wifiApChannel {1, DoReset, MinMaxValue, "wifiApChannel" }; + ConfigWrapper wifiApAuthmode{WIFI_AUTH_WPA2_PSK, DoReset, {}, "wifiApAuthmode" }; + + ConfigWrapper timeServerEnabled {true, DoReset, {}, "timeServerEnabl" }; + ConfigWrapper timeServer {"europe.pool.ntp.org", DoReset, StringMaxSize<64>, "timeServer" }; + ConfigWrapper timeSyncMode {SNTP_SYNC_MODE_IMMED, DoReset, {}, "timeSyncMode" }; + ConfigWrapper timeSyncInterval{espchrono::milliseconds32{CONFIG_LWIP_SNTP_UPDATE_DELAY}, DoReset, MinTimeSyncInterval, "timeSyncInterva" }; + ConfigWrapper timezoneOffset{espchrono::minutes32{60}, DoReset, {}, "timezoneOffset" }; // MinMaxValue + ConfigWrappertimeDst{espchrono::DayLightSavingMode::EuropeanSummerTime, DoReset, {}, "time_dst" }; + + template + void callForEveryConfig(T &&callable) + { +#define REGISTER_CONFIG(name) \ + if (callable(name)) return; + + REGISTER_CONFIG(baseMacAddressOverride) + REGISTER_CONFIG(hostname) + +#ifdef CONFIG_ETH_ENABLED + REGISTER_CONFIG(ethEnabled) + REGISTER_CONFIG(ethUseStaticIp) + REGISTER_CONFIG(ethStaticIp) + REGISTER_CONFIG(ethStaticSubnet) + REGISTER_CONFIG(ethStaticGateway) + REGISTER_CONFIG(ethUseStaticDns) + REGISTER_CONFIG(ethStaticDns0) + REGISTER_CONFIG(ethStaticDns1) + REGISTER_CONFIG(ethStaticDns2) +#endif + + REGISTER_CONFIG(wifiStaEnabled) + + for (auto &entry : wifi_configs) + { + REGISTER_CONFIG(entry.ssid) + REGISTER_CONFIG(entry.key) + REGISTER_CONFIG(entry.useStaticIp) + REGISTER_CONFIG(entry.staticIp) + REGISTER_CONFIG(entry.staticSubnet) + REGISTER_CONFIG(entry.staticGateway) + REGISTER_CONFIG(entry.useStaticDns) + REGISTER_CONFIG(entry.staticDns0) + REGISTER_CONFIG(entry.staticDns1) + REGISTER_CONFIG(entry.staticDns2) + } + + REGISTER_CONFIG(wifiStaMinRssi) + + REGISTER_CONFIG(wifiApEnabled) + REGISTER_CONFIG(wifiApName) + REGISTER_CONFIG(wifiApKey) + REGISTER_CONFIG(wifiApIp) + REGISTER_CONFIG(wifiApMask) + REGISTER_CONFIG(wifiApChannel) + REGISTER_CONFIG(wifiApAuthmode) + + REGISTER_CONFIG(timeServerEnabled) + REGISTER_CONFIG(timeServer) + REGISTER_CONFIG(timeSyncMode) + REGISTER_CONFIG(timeSyncInterval) + REGISTER_CONFIG(timezoneOffset) + REGISTER_CONFIG(timeDst) + +#undef REGISTER_API_VALUE + } +}; + +extern ConfigManager configs; diff --git a/main/debugconsole.cpp b/main/debugconsole.cpp new file mode 100644 index 0000000..fb31d94 --- /dev/null +++ b/main/debugconsole.cpp @@ -0,0 +1,141 @@ +#include "debugconsole.h" + +// system includes +#include + +// esp-idf includes +#include +#include + +// 3rdparty lib includes +#include + +namespace { +constexpr const char * const TAG = "DEBUG"; + +uint8_t consoleControlCharsReceived{}; +bool uart0Initialized{}; + +void handleNormalChar(char c); +void handleSpecialChar(char c); +} // namespace + +MemoryDebug memoryDebug{Off}; +espchrono::millis_clock::time_point lastMemoryDebug; + +void init_debugconsole() +{ + if (const auto result = uart_set_pin(UART_NUM_0, UART_PIN_NO_CHANGE, 3, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); result != ESP_OK) + { + ESP_LOGE(TAG, "uart_set_pin() failed with %s", esp_err_to_name(result)); + } + + if (const auto result = uart_driver_install(UART_NUM_0, SOC_UART_FIFO_LEN + 1, 0, 10, nullptr, 0); result != ESP_OK) + ESP_LOGE(TAG, "uart_driver_install() failed with %s", esp_err_to_name(result)); + else + uart0Initialized = true; +} + +void update_debugconsole() +{ + if (!uart0Initialized) + return; + + size_t length{}; + if (const auto result = uart_get_buffered_data_len(UART_NUM_0, &length); result != ESP_OK) + { + ESP_LOGW(TAG, "uart_get_buffered_data_len() failed with %s", esp_err_to_name(result)); + } + else if (length) + { + char data[length]; + length = uart_read_bytes(UART_NUM_0, data, length, 0); + + for (char c : std::string_view{data, length}) + { + if (consoleControlCharsReceived < 2) + { + switch (c) + { + case '\x1b': + if (consoleControlCharsReceived == 0) + consoleControlCharsReceived = 1; + else + consoleControlCharsReceived = 0; + break; + case '\x5b': + if (consoleControlCharsReceived == 1) + consoleControlCharsReceived = 2; + else + consoleControlCharsReceived = 0; + break; + default: + consoleControlCharsReceived = 0; + handleNormalChar(c); + } + } + else + { + consoleControlCharsReceived = 0; + handleSpecialChar(c); + } + } + } +} + +namespace { +void handleNormalChar(char c) +{ + constexpr auto rotateLogLevel = [](const char *tag){ + const auto oldLogLevel = esp_log_level_get(tag); + const auto newLogLevel = oldLogLevel == ESP_LOG_INFO ? ESP_LOG_DEBUG : (oldLogLevel == ESP_LOG_DEBUG ? ESP_LOG_VERBOSE : ESP_LOG_INFO); + esp_log_level_set(tag, newLogLevel); + ESP_LOGI(TAG, "%s loglevel set to %s (previous=%s)", tag, espcpputils::toString(newLogLevel).c_str(), espcpputils::toString(oldLogLevel).c_str()); + }; + + switch (c) + { + case 'r': case 'R': + ESP_LOGI(TAG, "Rebooting..."); + esp_restart(); + break; + case 'm': case 'M': + switch (memoryDebug) + { + case Off: + memoryDebug = Normal; + ESP_LOGI(TAG, "memory debug set to %s", "Normal"); + break; + case Normal: + memoryDebug = Fast; + ESP_LOGI(TAG, "memory debug set to %s", "Fast"); + break; + case Fast: + memoryDebug = Off; + ESP_LOGI(TAG, "memory debug set to %s", "Off"); + break; + } + break; + case 'w': case 'W': + rotateLogLevel("WEBSERVER"); + break; + } +} + +void handleSpecialChar(char c) +{ + switch (c) + { + case 'A': // Up arrow pressed + break; + case 'B': // Down arrow pressed + break; + case 'C': // Right arrow pressed + break; + case 'D': // Left arrow pressed + break; + default: + ESP_LOGI(TAG, "unknown control char received: %hhx", c); + } +} +} // namespace diff --git a/main/debugconsole.h b/main/debugconsole.h new file mode 100644 index 0000000..a72f7d1 --- /dev/null +++ b/main/debugconsole.h @@ -0,0 +1,11 @@ +#pragma once + +// 3rdparty lib includes +#include + +enum MemoryDebug {Off,Normal,Fast}; +extern MemoryDebug memoryDebug; +extern espchrono::millis_clock::time_point lastMemoryDebug; + +void init_debugconsole(); +void update_debugconsole(); diff --git a/main/main.cpp b/main/main.cpp index 6a6a9b1..317c7f6 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1,10 +1,191 @@ +#include "sdkconfig.h" + +// esp-idf includes #include +#include +#if defined(CONFIG_ESP_TASK_WDT_PANIC) || defined(CONFIG_ESP_TASK_WDT) +#include +#include +#endif +#ifdef CONFIG_APP_ROLLBACK_ENABLE +#include +#endif +#include + +// Arduino includes +#include + +// 3rdparty lib includes +#include +#include +#include +#include +#include +#include + +// local includes +#include "config.h" +#include "debugconsole.h" +#include "taskmanager.h" +#include "ota.h" +#include "wifi.h" + +using namespace std::chrono_literals; namespace { constexpr const char * const TAG = "MAIN"; + +espchrono::millis_clock::time_point lastLoopCount; +int _loopCount{}; +int loopCountTemp{}; } // namespace +const int &loopCount{_loopCount}; + extern "C" void app_main() { - ESP_LOGI(TAG, "hello world"); +#if defined(CONFIG_ESP_TASK_WDT_PANIC) || defined(CONFIG_ESP_TASK_WDT) + { + const auto taskHandle = xTaskGetCurrentTaskHandle(); + if (!taskHandle) + { + ESP_LOGE(TAG, "could not get handle to current main task!"); + } + else if (const auto result = esp_task_wdt_add(taskHandle); result != ESP_OK) + { + ESP_LOGE(TAG, "could not add main task to watchdog: %s", esp_err_to_name(result)); + } + } + + bool wasPreviouslyUpdating{}; +#endif + +#ifdef CONFIG_APP_ROLLBACK_ENABLE + esp_ota_img_states_t ota_state; + if (const esp_partition_t * const running = esp_ota_get_running_partition()) + { + if (const auto result = esp_ota_get_state_partition(running, &ota_state); result == ESP_ERR_NOT_FOUND) + ota_state = ESP_OTA_IMG_VALID; + else if (result != ESP_OK) + { + ESP_LOGE(TAG, "esp_ota_get_state_partition() failed with %s", esp_err_to_name(result)); + ota_state = ESP_OTA_IMG_UNDEFINED; + } + } + else + { + ESP_LOGE(TAG, "esp_ota_get_running_partition() returned nullptr"); + ota_state = ESP_OTA_IMG_UNDEFINED; + } +#endif + + //init proper ref tick value for PLL (uncomment if REF_TICK is different than 1MHz) + //ESP_REG(APB_CTRL_PLL_TICK_CONF_REG) = APB_CLK_FREQ / REF_CLK_FREQ - 1; + + pinMode(3, INPUT_PULLUP); + + if (const auto result = configs.init("dmxnode"); result != ESP_OK) + ESP_LOGE(TAG, "config_init_settings() failed with %s", esp_err_to_name(result)); + + for (const auto &task : schedulerTasks) + task.setup(); + + while (true) + { + bool pushStats = espchrono::ago(lastLoopCount) >= 1s; + if (pushStats) + { + lastLoopCount = espchrono::millis_clock::now(); + _loopCount = loopCountTemp; + loopCountTemp = 0; + } + + loopCountTemp++; + + for (auto &task : schedulerTasks) + { + task.loop(); + +#if defined(CONFIG_ESP_TASK_WDT_PANIC) || defined(CONFIG_ESP_TASK_WDT) + if (wasPreviouslyUpdating) + { + if (const auto result = esp_task_wdt_reset(); result != ESP_OK) + ESP_LOGE(TAG, "esp_task_wdt_reset() failed with %s", esp_err_to_name(result)); + //vPortYield(); + vTaskDelay(1); + } +#endif + } + + if (pushStats) + { + sched_pushStats(memoryDebug == Fast); + } + + if constexpr (LOG_LOCAL_LEVEL >= ESP_LOG_WARN) + { + const auto free8 = heap_caps_get_free_size(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); + if (free8 < 25000) + { + lastMemoryDebug = espchrono::millis_clock::now(); + ESP_LOGW(TAG, "MEMORY heap8=%zd (largest block: %zd) heap32=%zd lps=%i", + free8, + heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT), + heap_caps_get_free_size(MALLOC_CAP_INTERNAL|MALLOC_CAP_32BIT), + _loopCount + ); + } + else if constexpr (LOG_LOCAL_LEVEL >= ESP_LOG_INFO) + { + if (memoryDebug != Off && + espchrono::ago(lastMemoryDebug) >= (memoryDebug == Fast ? 100ms : 1000ms)) + { + lastMemoryDebug = espchrono::millis_clock::now(); + ESP_LOGI(TAG, "MEMORY heap8=%zd (largest block: %zd) heap32=%zd lps=%i", + free8, + heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT), + heap_caps_get_free_size(MALLOC_CAP_INTERNAL|MALLOC_CAP_32BIT), + _loopCount + ); + } + } + } + +#if defined(CONFIG_ESP_TASK_WDT_PANIC) || defined(CONFIG_ESP_TASK_WDT) + if (const auto result = esp_task_wdt_reset(); result != ESP_OK) + ESP_LOGE(TAG, "esp_task_wdt_reset() failed with %s", esp_err_to_name(result)); + + if (const auto isUpdating = otaClient.status() == OtaCloudUpdateStatus::Updating; isUpdating != wasPreviouslyUpdating) + { + wasPreviouslyUpdating = isUpdating; + + constexpr bool panic = +#ifdef CONFIG_ESP_TASK_WDT_PANIC + true +#else + false +#endif + ; + + const uint32_t timeout = isUpdating ? + CONFIG_ESP_TASK_WDT_TIMEOUT_S * 4 : + CONFIG_ESP_TASK_WDT_TIMEOUT_S; + + const auto result = esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, panic); + ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "esp_task_wdt_init() with new timeout %u returned: %s", timeout, esp_err_to_name(result)); + } +#endif + +#ifdef CONFIG_APP_ROLLBACK_ENABLE + if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) + { + const auto result = esp_ota_mark_app_valid_cancel_rollback(); + ESP_LOG_LEVEL_LOCAL((result == ESP_OK ? ESP_LOG_INFO : ESP_LOG_ERROR), TAG, "esp_ota_mark_app_valid_cancel_rollback() returned: %s", esp_err_to_name(result)); + ota_state = ESP_OTA_IMG_VALID; + } +#endif + + //vPortYield(); + vTaskDelay(1); + } } diff --git a/main/ota.cpp b/main/ota.cpp new file mode 100644 index 0000000..06b25c4 --- /dev/null +++ b/main/ota.cpp @@ -0,0 +1,58 @@ +#include "ota.h" + +// system includes +#include +#include + +// esp-idf includes +#include + +// 3rdparty lib includes +#include +#include +#include +#include +#include + +// local includes +#include "config.h" + +using namespace std::chrono_literals; + +namespace { +constexpr const char * const TAG = "OTA_CLOUD"; + +cpputils::DelayedConstruction _otaClient; +} // namespace + +EspAsyncOta &otaClient{_otaClient.getUnsafe()}; + +void ota_cloud_init() +{ + ESP_LOGI(TAG, "called"); + + _otaClient.construct("asyncOtaTask", 8192u); +} + +void ota_cloud_update() +{ + _otaClient->update(); +} + +tl::expected otaClientTrigger(std::string_view url) +{ + if (auto result = _otaClient->trigger(url, {}, {}, {}); !result) + return tl::make_unexpected(std::move(result).error()); + + wifi_stack::delete_scan_result(); + + return {}; +} + +tl::expected otaClientAbort() +{ + if (auto result = _otaClient->abort(); !result) + return tl::make_unexpected(std::move(result).error()); + + return {}; +} diff --git a/main/ota.h b/main/ota.h new file mode 100644 index 0000000..8d01137 --- /dev/null +++ b/main/ota.h @@ -0,0 +1,20 @@ +#pragma once + +// system includes +#include +#include +#include + +// 3rdparty lib includes +#include + +// forward declares +class EspAsyncOta; + +extern EspAsyncOta &otaClient; + +void ota_cloud_init(); +void ota_cloud_update(); + +tl::expected otaCloudTrigger(std::string_view url); +tl::expected otaCloudAbort(); diff --git a/main/taskmanager.cpp b/main/taskmanager.cpp new file mode 100644 index 0000000..a104c22 --- /dev/null +++ b/main/taskmanager.cpp @@ -0,0 +1,44 @@ +#include "taskmanager.h" + +// system includes +#include +#include + +// esp-idf includes +#include + +// 3rdparty lib includes +#include + +// local includes +#include "wifi.h" +#include "debugconsole.h" +#include "ota.h" + +using namespace std::chrono_literals; + +namespace { +constexpr const char * const TAG = "TASKS"; + +void not_needed() {} + +espcpputils::SchedulerTask schedulerTasksArr[] { + espcpputils::SchedulerTask { "wifi", wifi_begin, wifi_update, 100ms }, + espcpputils::SchedulerTask { "debugconsole", init_debugconsole, update_debugconsole, 50ms }, + espcpputils::SchedulerTask { "ota_cloud", ota_cloud_init, ota_cloud_update, 100ms }, +}; +} // namespace + +cpputils::ArrayView schedulerTasks{std::begin(schedulerTasksArr), std::end(schedulerTasksArr)}; + +void sched_pushStats(bool printTasks) +{ + if (printTasks) + ESP_LOGI(TAG, "begin listing tasks..."); + + for (auto &schedulerTask : schedulerTasks) + schedulerTask.pushStats(printTasks); + + if (printTasks) + ESP_LOGI(TAG, "end listing tasks"); +} diff --git a/main/taskmanager.h b/main/taskmanager.h new file mode 100644 index 0000000..6f428e4 --- /dev/null +++ b/main/taskmanager.h @@ -0,0 +1,11 @@ +#pragma once + +// 3rdparty lib includes +#include + +// forward declares +namespace espcpputils { class SchedulerTask; } + +extern cpputils::ArrayView schedulerTasks; + +void sched_pushStats(bool printTasks); diff --git a/main/wifi.cpp b/main/wifi.cpp new file mode 100644 index 0000000..b321640 --- /dev/null +++ b/main/wifi.cpp @@ -0,0 +1,146 @@ +#include "wifi.h" + +// system includes +#include + +// esp-idf includes +#include + +// 3rdparty lib includes +#include + +// local includes +#include "config.h" + +namespace { +wifi_stack::config createConfig(); +std::optional createStaConfig(); +wifi_stack::wifi_entry createWifiEntry(const WiFiConfig &wifi_config); +std::optional createApConfig(); +} // namespace + +void wifi_begin() +{ + wifi_stack::init(createConfig()); +} + +void wifi_update() +{ + wifi_stack::update(createConfig()); +} + +esp_err_t wifi_scan() +{ + const auto &sta_config = createStaConfig(); + if (!sta_config) + { + ESP_LOGE("BOBBY", "no sta enabled"); + return ESP_FAIL; + } + + if (const auto result = wifi_stack::begin_scan(*sta_config); !result) + { + ESP_LOGE("BOBBY", "begin_scan() failed with %.*s", result.error().size(), result.error().data()); + return ESP_FAIL; + } + + return ESP_OK; +} + +namespace { +wifi_stack::config createConfig() +{ + return wifi_stack::config { + .base_mac_override = configs.baseMacAddressOverride.value, + //.dual_ant = dual_ant_config{}, + .sta = createStaConfig(), + .ap = createApConfig(), +#ifdef CONFIG_ETH_ENABLED + .eth = createEthConfig() +#endif + }; +} + +std::optional createStaConfig() +{ + if (!configs.wifiStaEnabled.value) + return std::nullopt; + + return wifi_stack::sta_config{ + .hostname = configs.hostname.value, + .wifis = std::array { + createWifiEntry(configs.wifi_configs[0]), + createWifiEntry(configs.wifi_configs[1]), + createWifiEntry(configs.wifi_configs[2]), + createWifiEntry(configs.wifi_configs[3]), + createWifiEntry(configs.wifi_configs[4]), + createWifiEntry(configs.wifi_configs[5]), + createWifiEntry(configs.wifi_configs[6]), + createWifiEntry(configs.wifi_configs[7]), + createWifiEntry(configs.wifi_configs[8]), + createWifiEntry(configs.wifi_configs[9]) + }, + .min_rssi = configs.wifiStaMinRssi.value, + .long_range = false + }; +} + +wifi_stack::wifi_entry createWifiEntry(const WiFiConfig &wifi_config) +{ + std::optional static_ip; + if (wifi_config.useStaticIp.value) + static_ip = wifi_stack::static_ip_config { + .ip = wifi_config.staticIp.value, + .subnet = wifi_config.staticSubnet.value, + .gateway = wifi_config.staticGateway.value + }; + + wifi_stack::static_dns_config static_dns; + if (wifi_config.useStaticDns.value) + { + if (wifi_config.staticDns0.value.value()) + static_dns.main = wifi_config.staticDns0.value; + if (wifi_config.staticDns1.value.value()) + static_dns.backup = wifi_config.staticDns1.value; + if (wifi_config.staticDns2.value.value()) + static_dns.fallback = wifi_config.staticDns2.value; + } + + return wifi_stack::wifi_entry { + .ssid = wifi_config.ssid.value, + .key = wifi_config.key.value, + .static_ip = static_ip, + .static_dns = static_dns + }; +} + +std::optional createApConfig() +{ + if (!configs.wifiApEnabled.value) + return std::nullopt; + +// if (configs.wifiDisableApWhenOnline.value && +// cloudStarted && +// cloudConnected && +// lastCloudConnectedToggled && +// espchrono::ago(*lastCloudConnectedToggled) >= 30s) +// return std::nullopt; + + return wifi_stack::ap_config { + .hostname = configs.hostname.value, + .ssid = configs.wifiApName.value, + .key = configs.wifiApKey.value, + .static_ip = { + .ip = configs.wifiApIp.value, + .subnet = configs.wifiApMask.value, + .gateway = configs.wifiApIp.value, + }, + .channel = configs.wifiApChannel.value, + .authmode = configs.wifiApAuthmode.value, + .ssid_hidden = false, + .max_connection = 4, + .beacon_interval = 100, + .long_range = false + }; +} +} // namespace diff --git a/main/wifi.h b/main/wifi.h new file mode 100644 index 0000000..f063f72 --- /dev/null +++ b/main/wifi.h @@ -0,0 +1,13 @@ +#pragma once + +// esp-idf includes +#include + +// 3rdparty lib includes +#include + +void wifi_begin(); + +void wifi_update(); + +esp_err_t wifi_scan();