Firmware: Final work for the provisioner

This commit is contained in:
Erki 2024-01-23 10:47:33 +02:00
parent f3df59b0fe
commit 0ed3d69906
5 changed files with 153 additions and 16 deletions

View File

@ -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)

View File

@ -5,6 +5,11 @@
#pragma once
#include <expected>
#include <source_location>
#include <string_view>
#include <functional>
#include <utility>
#include <esp_err.h>
#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<typename Expected>
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<typename Expected, typename F>
auto unwrapOr(Expected&& expected, F&& f)
{
if (!expected.has_value())
std::invoke(std::forward<F>(f), expected.error());
else
return expected.value();
std::unreachable();
}

View File

@ -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 <inttypes.h>
#include <stdio.h>
#include <cinttypes>
#include <cstdio>
#include <expected>
#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()));
else
{
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
{
abortWithError("Main task notification timed out.");
}
}
const auto password = unwrapOrAbort(provisioner->getParameter("password")
.and_then([] (const auto& value)
{
return value.template getValue<etl::string<100>>();
}));
const auto ssid = unwrapOrAbort(provisioner->getParameter("ssid")
.and_then([] (const auto& value)
{
return value.template getValue<etl::string<100>>();
}));
// Pack up the provisioner.
delete provisioner;
printf("We now have a wifi with SSID: %s, password: %s\n", ssid.c_str(), password.c_str());
}

View File

@ -156,20 +156,23 @@ esp_err_t parametersGetHandler_(httpd_req_t* req)
}
std::expected<WifiProvisioner::Parameter::Value, esp_err_t> WifiProvisioner::Parameter::getValue(nvs::NVSHandle* file_handle) const
std::expected<WifiProvisioner::Parameter::Value, esp_err_t> 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<float>(value_raw);
data = std::bit_cast<float>(value_raw);
return data;
}
else if (type == Type::STRING)
{
@ -179,7 +182,9 @@ std::expected<WifiProvisioner::Parameter::Value, esp_err_t> 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<void, esp_err_t> WifiProvisioner::startProvisioning()
{
TRY(initializeWifiAp_());
@ -269,6 +284,28 @@ std::expected<void, esp_err_t> WifiProvisioner::startProvisioning()
return {};
}
const etl::vector<WifiProvisioner::Parameter, 10>& WifiProvisioner::getParameters()
{
if (!settings_initialized_)
abortWithError("WifiProvisioner read parameters without initialization.");
return params_;
}
std::expected<WifiProvisioner::Parameter, std::error_code> 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<void, std::error_code> WifiProvisioner::addParameter(const char* name, const char* nvs_name, const Parameter::Type type)
{
if (params_.full())

View File

@ -9,6 +9,7 @@
#include <etl/vector.h>
#include <etl/string.h>
#include <etl/delegate.h>
#include <cstdint>
#include <expected>
#include <variant>
#include <memory>
@ -53,7 +54,7 @@ public:
Type type;
using Value = std::variant<std::int32_t, float, etl::string<100>>;
using Value = std::variant<std::monostate, std::int32_t, float, etl::string<100>>;
Value data;
Parameter() = default;
@ -61,10 +62,43 @@ public:
: name(name)
, nvs_name(nvs_name)
, type(type)
, data(std::monostate())
{ }
std::expected<Value, esp_err_t> getValue(nvs::NVSHandle* file_handle) const;
std::expected<Value, esp_err_t> tryReadAndAssignValue(nvs::NVSHandle* file_handle);
std::expected<Value, const char*> tryValidateAndAssign(cJSON* object);
template<typename T>
std::expected<T, std::error_code> getValue() const
{
static_assert(std::is_same_v<T, std::int32_t> || std::is_same_v<T, float> || std::is_same_v<T, etl::string<100>>,
"T must be in the Value variant.");
if (holds_alternative<std::monostate>(data))
return std::unexpected(std::make_error_code(std::errc::io_error));
if constexpr (std::is_same_v<T, std::int32_t>)
{
if (type != Type::INT)
return std::unexpected(std::make_error_code(std::errc::invalid_argument));
return std::get<std::int32_t>(data);
}
else if constexpr (std::is_same_v<T, float>)
{
if (type != Type::FLOAT)
return std::unexpected(std::make_error_code(std::errc::invalid_argument));
return std::get<float>(data);
}
else
{
if (type != Type::STRING)
return std::unexpected(std::make_error_code(std::errc::invalid_argument));
return std::get<etl::string<100>>(data);
}
}
};
explicit WifiProvisioner(const char* settings_namespace, etl::delegate<void (const etl::vector<Parameter, 10>&)> success_cb);
@ -77,8 +111,10 @@ public:
WifiProvisioner& operator=(WifiProvisioner&&) = delete;
bool parametersAreConfigured();
void clearSettings();
std::expected<void, esp_err_t> startProvisioning();
const etl::vector<Parameter, 10>& getParameters();
std::expected<Parameter, std::error_code> getParameter(const etl::string<15>& name);
std::expected<void, std::error_code> addParameter(const char* name, const char* nvs_name, const Parameter::Type type);
CJsonPtr getParameterObjects() const;