Firmware: Final work for the provisioner
This commit is contained in:
parent
f3df59b0fe
commit
0ed3d69906
@ -3,4 +3,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
project(hello_world)
|
project(esp-vfd-clock)
|
||||||
|
|||||||
@ -5,6 +5,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <expected>
|
#include <expected>
|
||||||
|
#include <source_location>
|
||||||
|
#include <string_view>
|
||||||
|
#include <functional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <esp_err.h>
|
#include <esp_err.h>
|
||||||
|
|
||||||
#define TRY(x) ({ \
|
#define TRY(x) ({ \
|
||||||
@ -21,3 +26,33 @@
|
|||||||
return std::unexpected(_x); \
|
return std::unexpected(_x); \
|
||||||
_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();
|
||||||
|
}
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
#include "nvs.h"
|
#include "nvs.h"
|
||||||
#include "nvs_handle.hpp"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <cinttypes>
|
||||||
#include <stdio.h>
|
#include <cstdio>
|
||||||
#include <expected>
|
#include <expected>
|
||||||
|
|
||||||
|
#include "esp_expected.hpp"
|
||||||
#include "wifi_provisioner.hpp"
|
#include "wifi_provisioner.hpp"
|
||||||
|
|
||||||
extern "C" void app_main(void)
|
extern "C" void app_main(void)
|
||||||
@ -25,9 +25,10 @@ extern "C" void app_main(void)
|
|||||||
|
|
||||||
ESP_ERROR_CHECK(err);
|
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.");
|
printf("Settings successfully done.");
|
||||||
|
xTaskNotify(task_id, 0, eNoAction);
|
||||||
});
|
});
|
||||||
if (!provisioner->addParameter("SSID", "ssid", WifiProvisioner::Parameter::Type::STRING).has_value())
|
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();
|
auto result = provisioner->startProvisioning();
|
||||||
if (!result.has_value())
|
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
|
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<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());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
if (type == Type::INT)
|
||||||
{
|
{
|
||||||
std::int32_t value = 0;
|
std::int32_t value = 0;
|
||||||
TRY_ESP(file_handle->get_item(nvs_name.c_str(), value));
|
TRY_ESP(file_handle->get_item(nvs_name.c_str(), value));
|
||||||
|
data = value;
|
||||||
|
|
||||||
return value;
|
return data;
|
||||||
}
|
}
|
||||||
else if (type == Type::FLOAT)
|
else if (type == Type::FLOAT)
|
||||||
{
|
{
|
||||||
std::int32_t value_raw = 0;
|
std::int32_t value_raw = 0;
|
||||||
TRY_ESP(file_handle->get_item(nvs_name.c_str(), value_raw));
|
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)
|
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));
|
TRY_ESP(file_handle->get_string(nvs_name.c_str(), value.data(), value.size() - 1));
|
||||||
value.trim_to_terminator();
|
value.trim_to_terminator();
|
||||||
|
|
||||||
return value;
|
data = value;
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@ -248,9 +253,9 @@ bool WifiProvisioner::parametersAreConfigured()
|
|||||||
if (params_.empty())
|
if (params_.empty())
|
||||||
return false;
|
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())
|
!value.has_value())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -260,6 +265,16 @@ bool WifiProvisioner::parametersAreConfigured()
|
|||||||
return true;
|
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()
|
std::expected<void, esp_err_t> WifiProvisioner::startProvisioning()
|
||||||
{
|
{
|
||||||
TRY(initializeWifiAp_());
|
TRY(initializeWifiAp_());
|
||||||
@ -269,6 +284,28 @@ std::expected<void, esp_err_t> WifiProvisioner::startProvisioning()
|
|||||||
return {};
|
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)
|
std::expected<void, std::error_code> WifiProvisioner::addParameter(const char* name, const char* nvs_name, const Parameter::Type type)
|
||||||
{
|
{
|
||||||
if (params_.full())
|
if (params_.full())
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include <etl/vector.h>
|
#include <etl/vector.h>
|
||||||
#include <etl/string.h>
|
#include <etl/string.h>
|
||||||
#include <etl/delegate.h>
|
#include <etl/delegate.h>
|
||||||
|
#include <cstdint>
|
||||||
#include <expected>
|
#include <expected>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -53,7 +54,7 @@ public:
|
|||||||
|
|
||||||
Type type;
|
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;
|
Value data;
|
||||||
|
|
||||||
Parameter() = default;
|
Parameter() = default;
|
||||||
@ -61,10 +62,43 @@ public:
|
|||||||
: name(name)
|
: name(name)
|
||||||
, nvs_name(nvs_name)
|
, nvs_name(nvs_name)
|
||||||
, type(type)
|
, 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);
|
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);
|
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;
|
WifiProvisioner& operator=(WifiProvisioner&&) = delete;
|
||||||
|
|
||||||
bool parametersAreConfigured();
|
bool parametersAreConfigured();
|
||||||
|
void clearSettings();
|
||||||
std::expected<void, esp_err_t> startProvisioning();
|
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);
|
std::expected<void, std::error_code> addParameter(const char* name, const char* nvs_name, const Parameter::Type type);
|
||||||
CJsonPtr getParameterObjects() const;
|
CJsonPtr getParameterObjects() const;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user