Skip to content

Commit

Permalink
feat: OTA Routine
Browse files Browse the repository at this point in the history
  • Loading branch information
abrondijk committed Jan 29, 2025
1 parent 19fdcf9 commit ec718a6
Show file tree
Hide file tree
Showing 3 changed files with 345 additions and 35 deletions.
4 changes: 2 additions & 2 deletions components/filesystem/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
idf_component_register(
SRCS "src/Filesystem.cpp"
INCLUDE_DIRS "include"
REQUIRES manager
PRIV_REQUIRES util esp_littlefs app_update)
REQUIRES manager http_server semantic_versioning
PRIV_REQUIRES util esp_littlefs app_update esp_http_server mbedtls spi_flash)
71 changes: 53 additions & 18 deletions components/filesystem/include/Filesystem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,26 @@
#include <etl/string.h>
#include <sys/_default_fcntl.h>

#include <expected>
#include <fstream>
#include <span>
#include <string>

#include "Component.hpp"
#include "HttpServer.hpp"
#include "esp_err.h"
#include "esp_log.h"
#include "semantic_versioning.hpp"

namespace sdk::Http {
class Server;
}
class Filesystem final : public sdk::Component {
public:
Filesystem() = default;
~Filesystem() = default;

explicit Filesystem(const sdk::Http::Server& httpServer) : m_httpServer(httpServer) {}

/* Component override functions */
etl::string<50> getTag() override { return TAG; };
Status getStatus() override;
Expand All @@ -24,7 +32,7 @@ class Filesystem final : public sdk::Component {
Status stop() override;

// First is the size of the partition in bytes, second is the amount of used space in bytes
using PartitionInfo = std::pair<std::size_t, std::size_t>;
using PartitionInfo = std::pair<size_t, size_t>;

/**
* Write a file to the filesystem
Expand All @@ -34,14 +42,14 @@ class Filesystem final : public sdk::Component {
* @param data The data to write to the file
* @return std::error_code
*/
template<std::size_t FILEPATH_SIZE, std::size_t FILESIZE>
static std::error_code writeFile(const etl::string<FILEPATH_SIZE>& filepath, const etl::string<FILESIZE>& data) noexcept {
const int fd = open(filepath.c_str(), O_WRONLY | O_CREAT, 0644);
template<size_t FILEPATH_SIZE, size_t FILESIZE>
static std::error_code writeFile(const etl::string_view filepath, const etl::string_view data) noexcept {
const int fd = open(filepath.data(), O_WRONLY | O_CREAT, 0644);
if (fd == -1) {
return std::error_code(errno, std::generic_category());
}

if (const auto ret = write(fd, data.c_str(), data.size()); ret == -1) {
if (const auto ret = write(fd, data.data(), data.size()); ret == -1) {
close(fd);
return std::error_code(errno, std::generic_category());
}
Expand All @@ -52,20 +60,19 @@ class Filesystem final : public sdk::Component {

/**
* Read a file from the filesystem
* @tparam FILEPATH_SIZE Length of the filepath
* @tparam FILESIZE Length of the data to read
* @param filepath The path to the file to read
* @return std::expected<etl::string<FILESIZE>, std::error_code>
*/
template<std::size_t FILEPATH_SIZE, std::size_t FILESIZE>
static std::expected<etl::string<FILESIZE>, std::error_code> readFile(const etl::string<FILEPATH_SIZE>& filepath) noexcept {
template<size_t FILESIZE>
static std::expected<etl::string<FILESIZE>, std::error_code> readFile(const etl::string_view filepath) noexcept {
auto filesize = fileSize(filepath);
if (!filesize.has_value()) {
return std::unexpected(filesize.error());
}
assert(filesize.value() <= FILESIZE && "File is too large to read into buffer");

std::ifstream inFile(filepath.c_str(), std::ios::in | std::ios::binary);
std::ifstream inFile(filepath.data(), std::ios::in | std::ios::binary);
if (!inFile) {
return std::unexpected(std::error_code(errno, std::generic_category()));
}
Expand Down Expand Up @@ -98,7 +105,8 @@ class Filesystem final : public sdk::Component {

static inline auto m_status = Status::UNINITIALIZED;

static inline etl::string<13> otaAssetsLabel = "";
static inline etl::string<13> m_currentBootPartitionLabel = "";
static inline etl::string<13> m_currentAssetsLabel = "";

static constexpr auto OTA_A_LABEL = "ota_a";
static constexpr auto OTA_A_MAIN_PARTITION_LABEL = "ota_a_assets";
Expand All @@ -108,30 +116,57 @@ class Filesystem final : public sdk::Component {
static constexpr auto STATIC_PARTITION_LABEL = "static_assets";
static constexpr auto STATIC_PARTITION_MOUNT_POINT = "/static_assets";

struct OtaInfoHeader {
size_t appSize;
std::array<uint8_t, 32> appSha;
size_t otaAssetsSize;
std::array<uint8_t, 32> otaAssetsSha;
bool hasStaticAssets;
size_t staticAssetsSize;
std::array<uint8_t, 32> staticAssetsSha;
char firmwareVersion[20];
};

const sdk::Http::Server& m_httpServer;

/**
* Get the size of a file
* @tparam FILEPATH_SIZE Length of the filepath
* @param filepath The path to the file to get the size of
* @return std::expected<std::size_t, std::error_code>
* @return std::expected<size_t, std::error_code>
*/
template<std::size_t FILEPATH_SIZE>
static std::expected<std::size_t, std::error_code> fileSize(const etl::string<FILEPATH_SIZE>& filepath) noexcept {
static std::expected<size_t, std::error_code> fileSize(const etl::string_view& filepath) noexcept {
struct stat st;
if (stat(filepath.c_str(), &st) == -1) {
if (stat(filepath.data(), &st) == -1) {
return std::unexpected(std::error_code(errno, std::generic_category()));
}
return st.st_size;
}

static esp_err_t otaHandler(httpd_req_t* req);

static esp_err_t otaWritePartition(httpd_req_t* req, const esp_partition_t* partition, const size_t updateSize, const std::span<uint8_t> sha256, std::span<char> buffer);

static void restartTask(void*);

static esp_err_t receiveBuffer(httpd_req_t* req, const std::span<char> buffer, const size_t length);

static bool verifyVersionNumber(const std::span<char> sha256);

/**
* Get information about a partition
* @param partitionLabel The label of the partition to get information about
* @return std::expected<PartitionInfo, std::error_code>
*/
static std::expected<PartitionInfo, std::error_code> partitionInfo(const etl::string<20>& partitionLabel);

static Status registerOTA();
static Status unregisterOTA();
std::error_code registerOTA() const;

static esp_err_t handleOtaError(httpd_req_t* req, const esp_err_t error, const etl::string_view message, const httpd_err_code_t code);
static void handleOtaError(httpd_req_t* req, const etl::string_view message, const httpd_err_code_t code);
static etl::string<64> sha256ToString(const std::span<uint8_t> sha256);


static bool verifyEntirePartition(const esp_partition_t* partition, const std::span<uint8_t> sha256);
};

#endif /* FILESYSTEM_HPP */
Loading

0 comments on commit ec718a6

Please sign in to comment.