diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index 0a454d0..65afe25 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -3,4 +3,4 @@ cmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(hello_world) +project(esp-vfd-clock) diff --git a/firmware/main/esp_expected.hpp b/firmware/main/esp_expected.hpp index af9406a..ec5e3f4 100644 --- a/firmware/main/esp_expected.hpp +++ b/firmware/main/esp_expected.hpp @@ -5,6 +5,11 @@ #pragma once #include +#include +#include +#include +#include + #include #define TRY(x) ({ \ @@ -21,3 +26,33 @@ return std::unexpected(_x); \ _x; \ }) + +[[noreturn]] inline void abortWithError(const char* error, const std::source_location location = std::source_location::current()) +{ + esp_rom_printf("abortWithError called, reason: %s\n", error); + esp_rom_printf("file: \"%s\" line %d\n", location.file_name(), location.line()); + if (location.function_name() != nullptr) + esp_rom_printf("func: %s\n", location.function_name()); + + abort(); +} + +template +auto unwrapOrAbort(Expected&& expected, const std::source_location location = std::source_location::current()) +{ + if (!expected.has_value()) + abortWithError("Unwrapped an errored expected.", location); + else + return expected.value(); +} + +template +auto unwrapOr(Expected&& expected, F&& f) +{ + if (!expected.has_value()) + std::invoke(std::forward(f), expected.error()); + else + return expected.value(); + + std::unreachable(); +} diff --git a/firmware/main/main.cpp b/firmware/main/main.cpp index 910c47b..2b10f7b 100644 --- a/firmware/main/main.cpp +++ b/firmware/main/main.cpp @@ -1,15 +1,15 @@ #include "nvs_flash.h" #include "nvs.h" -#include "nvs_handle.hpp" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "sdkconfig.h" -#include -#include +#include +#include #include +#include "esp_expected.hpp" #include "wifi_provisioner.hpp" extern "C" void app_main(void) @@ -25,9 +25,10 @@ extern "C" void app_main(void) ESP_ERROR_CHECK(err); - auto* provisioner = new WifiProvisioner("wifi_settings", [](const auto& params) + auto* provisioner = new WifiProvisioner("wifi_settings", [task_id = xTaskGetCurrentTaskHandle()](const auto& params) { printf("Settings successfully done."); + xTaskNotify(task_id, 0, eNoAction); }); if (!provisioner->addParameter("SSID", "ssid", WifiProvisioner::Parameter::Type::STRING).has_value()) { @@ -45,8 +46,36 @@ extern "C" void app_main(void) { auto result = provisioner->startProvisioning(); if (!result.has_value()) - printf("Error: %s\n", esp_err_to_name(result.error())); + { + ESP_ERROR_CHECK(result.error()); + } + + printf("Provisioning started...\n"); + + const auto notification_received = xTaskNotifyWait(pdFALSE, ULONG_MAX, nullptr, portMAX_DELAY); + if (notification_received == pdPASS) + { + printf("Notification happened."); + } else - printf("Provisioning started...\n"); + { + abortWithError("Main task notification timed out."); + } } + + const auto password = unwrapOrAbort(provisioner->getParameter("password") + .and_then([] (const auto& value) + { + return value.template getValue>(); + })); + const auto ssid = unwrapOrAbort(provisioner->getParameter("ssid") + .and_then([] (const auto& value) + { + return value.template getValue>(); + })); + + // Pack up the provisioner. + delete provisioner; + + printf("We now have a wifi with SSID: %s, password: %s\n", ssid.c_str(), password.c_str()); } diff --git a/firmware/main/wifi_provisioner.cpp b/firmware/main/wifi_provisioner.cpp index 8416962..93c434f 100644 --- a/firmware/main/wifi_provisioner.cpp +++ b/firmware/main/wifi_provisioner.cpp @@ -156,20 +156,23 @@ esp_err_t parametersGetHandler_(httpd_req_t* req) } -std::expected WifiProvisioner::Parameter::getValue(nvs::NVSHandle* file_handle) const +std::expected WifiProvisioner::Parameter::tryReadAndAssignValue(nvs::NVSHandle* file_handle) { if (type == Type::INT) { std::int32_t value = 0; TRY_ESP(file_handle->get_item(nvs_name.c_str(), value)); + data = value; - return value; + return data; } else if (type == Type::FLOAT) { std::int32_t value_raw = 0; TRY_ESP(file_handle->get_item(nvs_name.c_str(), value_raw)); - return std::bit_cast(value_raw); + data = std::bit_cast(value_raw); + + return data; } else if (type == Type::STRING) { @@ -179,7 +182,9 @@ std::expected WifiProvisioner::Par TRY_ESP(file_handle->get_string(nvs_name.c_str(), value.data(), value.size() - 1)); value.trim_to_terminator(); - return value; + data = value; + + return data; } return ESP_FAIL; @@ -248,9 +253,9 @@ bool WifiProvisioner::parametersAreConfigured() if (params_.empty()) return false; - for (const auto& param : params_) + for (auto& param : params_) { - if (const auto value = param.getValue(file_handle_.get()); + if (const auto value = param.tryReadAndAssignValue(file_handle_.get()); !value.has_value()) { return false; @@ -260,6 +265,16 @@ bool WifiProvisioner::parametersAreConfigured() return true; } +void WifiProvisioner::clearSettings() +{ + if (!settings_initialized_) + return; + + settings_initialized_ = false; + ESP_ERROR_CHECK(file_handle_->set_item(NVS_IS_INITED, settings_initialized_)); + ESP_ERROR_CHECK(file_handle_->commit()); +} + std::expected WifiProvisioner::startProvisioning() { TRY(initializeWifiAp_()); @@ -269,6 +284,28 @@ std::expected WifiProvisioner::startProvisioning() return {}; } +const etl::vector& WifiProvisioner::getParameters() +{ + if (!settings_initialized_) + abortWithError("WifiProvisioner read parameters without initialization."); + + return params_; +} + +std::expected WifiProvisioner::getParameter(const etl::string<15>& name) +{ + if (!settings_initialized_) + return std::unexpected{std::make_error_code(std::errc::not_connected)}; + + for (const auto& param : params_) + { + if (param.nvs_name == name) + return param; + } + + return std::unexpected{std::make_error_code(std::errc::invalid_argument)}; +} + std::expected WifiProvisioner::addParameter(const char* name, const char* nvs_name, const Parameter::Type type) { if (params_.full()) diff --git a/firmware/main/wifi_provisioner.hpp b/firmware/main/wifi_provisioner.hpp index d583ad4..9551232 100644 --- a/firmware/main/wifi_provisioner.hpp +++ b/firmware/main/wifi_provisioner.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -53,7 +54,7 @@ public: Type type; - using Value = std::variant>; + using Value = std::variant>; Value data; Parameter() = default; @@ -61,10 +62,43 @@ public: : name(name) , nvs_name(nvs_name) , type(type) + , data(std::monostate()) { } - std::expected getValue(nvs::NVSHandle* file_handle) const; + std::expected tryReadAndAssignValue(nvs::NVSHandle* file_handle); std::expected tryValidateAndAssign(cJSON* object); + + template + std::expected getValue() const + { + static_assert(std::is_same_v || std::is_same_v || std::is_same_v>, + "T must be in the Value variant."); + + if (holds_alternative(data)) + return std::unexpected(std::make_error_code(std::errc::io_error)); + + if constexpr (std::is_same_v) + { + if (type != Type::INT) + return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + + return std::get(data); + } + else if constexpr (std::is_same_v) + { + if (type != Type::FLOAT) + return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + + return std::get(data); + } + else + { + if (type != Type::STRING) + return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + + return std::get>(data); + } + } }; explicit WifiProvisioner(const char* settings_namespace, etl::delegate&)> success_cb); @@ -77,8 +111,10 @@ public: WifiProvisioner& operator=(WifiProvisioner&&) = delete; bool parametersAreConfigured(); - + void clearSettings(); std::expected startProvisioning(); + const etl::vector& getParameters(); + std::expected getParameter(const etl::string<15>& name); std::expected addParameter(const char* name, const char* nvs_name, const Parameter::Type type); CJsonPtr getParameterObjects() const;