diff --git a/.gitmodules b/.gitmodules index e642f64a..09bc02f6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,6 @@ [submodule "thirdparty/esp-idf-components/embedded_cli/EmbeddedCLI"] path = thirdparty/esp-idf-components/embedded_cli/EmbeddedCLI url = https://github.com/AndreRenaud/EmbeddedCLI.git +[submodule "thirdparty/uWebSockets/uWebSockets"] + path = thirdparty/uWebSockets/uWebSockets + url = https://github.com/uNetworking/uWebSockets.git diff --git a/effects/CMakeLists.txt b/effects/CMakeLists.txt new file mode 100644 index 00000000..eb68ea90 --- /dev/null +++ b/effects/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(abstract_dsp) +add_subdirectory(chorus) +add_subdirectory(gate) +add_subdirectory(valvestate) +add_subdirectory(wah) diff --git a/firmware/.idea/.gitignore b/firmware/.idea/.gitignore deleted file mode 100644 index 13566b81..00000000 --- a/firmware/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/firmware/.idea/.name b/firmware/.idea/.name deleted file mode 100644 index 8a3960e9..00000000 --- a/firmware/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -esp32-dsp \ No newline at end of file diff --git a/firmware/.idea/codeStyles/Project.xml b/firmware/.idea/codeStyles/Project.xml deleted file mode 100644 index 7cc81a4f..00000000 --- a/firmware/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/firmware/.idea/codeStyles/codeStyleConfig.xml b/firmware/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123c..00000000 --- a/firmware/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/firmware/.idea/discord.xml b/firmware/.idea/discord.xml deleted file mode 100644 index d8e95616..00000000 --- a/firmware/.idea/discord.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/firmware/.idea/firmware.iml b/firmware/.idea/firmware.iml deleted file mode 100644 index f08604bb..00000000 --- a/firmware/.idea/firmware.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/firmware/.idea/misc.xml b/firmware/.idea/misc.xml deleted file mode 100644 index 4421b5f9..00000000 --- a/firmware/.idea/misc.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/firmware/.idea/modules.xml b/firmware/.idea/modules.xml deleted file mode 100644 index cb860b65..00000000 --- a/firmware/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/firmware/.idea/protoeditor.xml b/firmware/.idea/protoeditor.xml deleted file mode 100644 index 37f5878a..00000000 --- a/firmware/.idea/protoeditor.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/firmware/.idea/vcs.xml b/firmware/.idea/vcs.xml deleted file mode 100644 index 4b3dd066..00000000 --- a/firmware/.idea/vcs.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index dcb434af..1b56bace 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -1,58 +1,35 @@ -if(ESP_PLATFORM) - cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.13) - set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 20) - option(SHRAPNEL_RESET_WIFI_CREDENTIALS - "Remove the provisioned Wi-Fi credentials at power up" - OFF - ) +option(SHRAPNEL_RESET_WIFI_CREDENTIALS + "Remove the provisioned Wi-Fi credentials at power up" + OFF +) - if(SHRAPNEL_RESET_WIFI_CREDENTIALS) - add_compile_definitions(SHRAPNEL_RESET_WIFI_CREDENTIALS=1) - endif() - - list(APPEND EXTRA_COMPONENT_DIRS ../thirdparty/esp-idf-components/esp-dsp) - - include($ENV{IDF_PATH}/tools/cmake/project.cmake) - project(esp32-dsp) - - add_subdirectory(../effects/abstract_dsp abstract_dsp) - add_subdirectory(../effects/chorus chorus) - add_subdirectory(../effects/gate gate) - add_subdirectory(../effects/valvestate valvestate) - add_subdirectory(../effects/wah wah) - add_subdirectory(../thirdparty/esp-idf-components/cppcodec cppcodec) - add_subdirectory(../thirdparty/esp-idf-components/embedded_cli embedded_cli) - add_subdirectory(../thirdparty/esp-idf-components/esp32-fft esp32-fft) - add_subdirectory(../thirdparty/esp-idf-components/etl etl) - add_subdirectory(../thirdparty/esp-idf-components/nanopb nanopb) - add_subdirectory(components/api) - add_subdirectory(components/audio) - add_subdirectory(components/audio_events) - add_subdirectory(components/audio_param) - add_subdirectory(components/cmd_handling) - add_subdirectory(components/compiler_warning_flags) - add_subdirectory(components/esp_persistence) - add_subdirectory(components/float_convert) - add_subdirectory(components/hardware) - add_subdirectory(components/heap_tracing) - add_subdirectory(components/i2c) - add_subdirectory(components/i2s) - add_subdirectory(components/messages) - add_subdirectory(components/midi_handling) - add_subdirectory(components/midi_mapping) - add_subdirectory(components/midi_protocol) - add_subdirectory(components/os) - add_subdirectory(components/pcm3060) - add_subdirectory(components/persistence) - add_subdirectory(components/presets) - add_subdirectory(components/server) - add_subdirectory(components/shrapnel_console) - add_subdirectory(components/wifi) - - return() +if(SHRAPNEL_RESET_WIFI_CREDENTIALS) + add_compile_definitions(SHRAPNEL_RESET_WIFI_CREDENTIALS=1) endif() -add_subdirectory(main) -add_subdirectory(components) +list(APPEND EXTRA_COMPONENT_DIRS ../thirdparty/esp-idf-components/esp-dsp) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(esp32-dsp) + +add_subdirectory(../effects effects) +add_subdirectory(../shrapnel shrapnel) +add_subdirectory(../thirdparty/esp-idf-components/cppcodec cppcodec) +add_subdirectory(../thirdparty/esp-idf-components/embedded_cli embedded_cli) +add_subdirectory(../thirdparty/esp-idf-components/esp32-fft esp32-fft) +add_subdirectory(../thirdparty/esp-idf-components/etl etl) +add_subdirectory(../thirdparty/esp-idf-components/nanopb nanopb) +add_subdirectory(components/esp_midi_uart) +add_subdirectory(components/esp_persistence) +add_subdirectory(components/hardware) +add_subdirectory(components/heap_tracing) +add_subdirectory(components/i2c) +add_subdirectory(components/i2s) +add_subdirectory(components/pcm3060) +add_subdirectory(components/server) +add_subdirectory(components/shrapnel_console) +add_subdirectory(components/wifi) diff --git a/firmware/components/CMakeLists.txt b/firmware/components/CMakeLists.txt index a925f8a9..e69de29b 100644 --- a/firmware/components/CMakeLists.txt +++ b/firmware/components/CMakeLists.txt @@ -1,18 +0,0 @@ -if(ESP_PLATFORM) - return() -endif() -add_subdirectory(api) -add_subdirectory(audio_events) -add_subdirectory(audio_param) -add_subdirectory(cmd_handling) -add_subdirectory(compiler_warning_flags) -add_subdirectory(float_convert) -add_subdirectory(hardware) -add_subdirectory(heap_tracing) -add_subdirectory(messages) -add_subdirectory(midi_handling) -add_subdirectory(midi_mapping) -add_subdirectory(midi_protocol) -add_subdirectory(os) -add_subdirectory(persistence) -add_subdirectory(presets) diff --git a/firmware/components/cmd_handling/include/cmd_handling.h b/firmware/components/cmd_handling/include/cmd_handling.h deleted file mode 100644 index 57a5da90..00000000 --- a/firmware/components/cmd_handling/include/cmd_handling.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2022 Barabas Raffai - * - * This file is part of ShrapnelDSP. - * - * ShrapnelDSP is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * ShrapnelDSP is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * ShrapnelDSP. If not, see . - */ - -#pragma once - -#include "audio_param.h" -#include "cmd_handling_api.h" -#include "esp_err.h" -#include "esp_log.h" -#include "etl/list.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include -#include -#include -#include -#include -#include - -namespace shrapnel::parameters { - -template -class CommandHandling final -{ -public: - using SendMessageCallback = - etl::delegate)>; - - /** \brief - * - * \param[in] a_param Data received through \ref dispatch() is - * translated to binary and sent to this object. - */ - CommandHandling(std::shared_ptr a_param, - SendMessageCallback a_send_message) - : param(a_param), - send_message(a_send_message) - { - } - - void dispatch(const ApiMessage &a_message, int fd) - { -#if !defined(TESTING) - ESP_LOGI(TAG, - "%s stack %d", - __FUNCTION__, - uxTaskGetStackHighWaterMark(NULL)); -#endif - - std::visit( - [&](const auto &message) -> void - { - using T = std::decay_t; - - if constexpr(std::is_same_v) - { - parameter_update(message, fd); - } - else if constexpr(std::is_same_v) - { - initialise_parameters(); - } - else - { - ESP_LOGE(TAG, "Unhandled message type"); - } - }, - a_message); - } - -private: - void parameter_update(const Update &message, int fd) - { - int rc = param->update(message.id, message.value); - if(rc != 0) - { - ESP_LOGE(TAG, - "Failed to update parameter (%s) with value %f", - message.id.data(), - message.value); - } - - send_message(message, fd); - } - - void initialise_parameters() - { - for(const auto &[key, value] : *param) - { - Update message = { - .id{id_t{key}}, - .value{value->get()}, - }; - - send_message(message, std::nullopt); - } - } - - std::shared_ptr param; - SendMessageCallback send_message; - - static inline const char *TAG = "cmd_handling"; -}; - -} // namespace shrapnel::parameters diff --git a/firmware/components/esp_midi_uart/CMakeLists.txt b/firmware/components/esp_midi_uart/CMakeLists.txt new file mode 100644 index 00000000..f69d7fc3 --- /dev/null +++ b/firmware/components/esp_midi_uart/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(shrapnel_esp_midi_uart STATIC) +add_library(shrapnel::esp_midi_uart ALIAS shrapnel_esp_midi_uart) + +target_sources(shrapnel_esp_midi_uart PRIVATE src/esp_midi_uart.cpp) + +target_include_directories(shrapnel_esp_midi_uart PUBLIC include) + +target_link_libraries(shrapnel_esp_midi_uart PUBLIC + shrapnel::midi_protocol + idf::freertos + idf::driver +) \ No newline at end of file diff --git a/firmware/components/midi_protocol/include/midi_uart.h b/firmware/components/esp_midi_uart/include/esp_midi_uart.h similarity index 77% rename from firmware/components/midi_protocol/include/midi_uart.h rename to firmware/components/esp_midi_uart/include/esp_midi_uart.h index 57ade201..1cb131c8 100644 --- a/firmware/components/midi_protocol/include/midi_uart.h +++ b/firmware/components/esp_midi_uart/include/esp_midi_uart.h @@ -20,22 +20,13 @@ #pragma once #include "freertos/FreeRTOS.h" -#include +#include "midi_uart.h" #include #include -#include -#include -namespace shrapnel { -namespace midi { +namespace shrapnel::midi { -class MidiUartBase -{ -public: - virtual std::optional get_byte(TickType_t timeout) = 0; -}; - -class EspMidiUart final : public MidiUartBase +class EspMidiUart final : public MidiUartBase { public: EspMidiUart(uart_port_t uart, gpio_num_t rx_io); @@ -46,5 +37,4 @@ class EspMidiUart final : public MidiUartBase uart_port_t uart; }; -} // namespace midi -} // namespace shrapnel +} // namespace shrapnel::midi \ No newline at end of file diff --git a/firmware/components/midi_protocol/src/midi_uart.cpp b/firmware/components/esp_midi_uart/src/esp_midi_uart.cpp similarity index 97% rename from firmware/components/midi_protocol/src/midi_uart.cpp rename to firmware/components/esp_midi_uart/src/esp_midi_uart.cpp index e56841d1..58f70cd5 100644 --- a/firmware/components/midi_protocol/src/midi_uart.cpp +++ b/firmware/components/esp_midi_uart/src/esp_midi_uart.cpp @@ -17,7 +17,7 @@ * ShrapnelDSP. If not, see . */ -#include "midi_uart.h" +#include "esp_midi_uart.h" #include namespace shrapnel { @@ -62,4 +62,4 @@ std::optional EspMidiUart::get_byte(TickType_t timeout) } } // namespace midi -} // namespace shrapnel +} // namespace shrapnel \ No newline at end of file diff --git a/firmware/components/messages/include/messages.h b/firmware/components/messages/include/messages.h deleted file mode 100644 index 90ae889b..00000000 --- a/firmware/components/messages/include/messages.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2022 Barabas Raffai - * - * This file is part of ShrapnelDSP. - * - * ShrapnelDSP is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * ShrapnelDSP is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * ShrapnelDSP. If not, see . - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -using ApiMessage = - std::variant; -using FileDescriptor = std::optional; -using AppMessage = std::pair; - -namespace shrapnel::api { - -template <> -std::optional> to_bytes(const ApiMessage &message, - std::span buffer); - -template <> -std::optional from_bytes(std::span buffer); - -} // namespace shrapnel::api \ No newline at end of file diff --git a/firmware/components/server/include/server.h b/firmware/components/server/include/server.h index 69c7e995..fce3b411 100644 --- a/firmware/components/server/include/server.h +++ b/firmware/components/server/include/server.h @@ -30,18 +30,22 @@ namespace shrapnel { class Server { public: - Server(QueueBase *in_queue, QueueBase *out_queue); + Server(etl::delegate &in, + uint32_t time_to_wait)> output_message, + QueueBase>> *out_queue); void start(); void stop(); - void send_message(const AppMessage &message); + void send_message(const std::pair> &message); private: httpd_handle_t server = nullptr; - QueueBase *in_queue; - QueueBase *out_queue; + etl::delegate &in, + uint32_t time_to_wait)> + output_message; + QueueBase>> *out_queue; /* * TODO espressif's http server drops some calls to the work function when @@ -53,7 +57,8 @@ class Server SemaphoreHandle_t work_semaphore; friend esp_err_t websocket_get_handler(httpd_req_t *req); friend void websocket_send(void *arg); - friend void send_websocket_message(Server &self, const AppMessage &message); + friend void send_websocket_message( + Server &self, const std::pair> &message); }; } // namespace shrapnel diff --git a/firmware/components/server/src/server.cpp b/firmware/components/server/src/server.cpp index 5b8c5ecc..c693fa0f 100644 --- a/firmware/components/server/src/server.cpp +++ b/firmware/components/server/src/server.cpp @@ -34,10 +34,12 @@ namespace shrapnel { static void debug_print_sent_message(const ApiMessage &message); static void debug_print_received_message(const ApiMessage &message); -Server::Server(QueueBase *a_in_queue, - QueueBase *a_out_queue) +Server::Server( + etl::delegate &in, + uint32_t time_to_wait)> a_output_message, + QueueBase>> *a_out_queue) { - in_queue = a_in_queue; + output_message = a_output_message; out_queue = a_out_queue; work_semaphore = xSemaphoreCreateBinary(); assert(work_semaphore); @@ -145,12 +147,8 @@ esp_err_t websocket_get_handler(httpd_req_t *req) if(message.has_value()) { debug_print_received_message(*message); - auto out = AppMessage{*message, fd}; - int queue_rc = self->in_queue->send(&out, pdMS_TO_TICKS(100)); - if(queue_rc != pdPASS) - { - ESP_LOGE(TAG, "in_queue message dropped"); - } + auto out = std::pair{*message, fd}; + self->output_message(out, pdMS_TO_TICKS(100)); } else { @@ -168,9 +166,9 @@ void websocket_send(void *arg) { auto self = reinterpret_cast(arg); - AppMessage message; - int rc = self->out_queue->receive(&message, 0); - if(!rc) + std::pair> message; + auto rc = self->out_queue->receive(&message, 0); + if(rc != queue_error::SUCCESS) { ESP_LOGE(TAG, "%s failed to receive from queue", __FUNCTION__); return; @@ -192,7 +190,8 @@ void websocket_send(void *arg) xSemaphoreGive(self->work_semaphore); } -void send_websocket_message(Server &self, const AppMessage &message) +void send_websocket_message( + Server &self, const std::pair> &message) { std::array memory{}; auto buffer = std::span{memory}; @@ -259,34 +258,25 @@ static void debug_print_sent_message(const ApiMessage &message) { etl::string<128> debug; etl::string_stream debug_stream{debug}; - std::visit( - [&](const auto &message) -> void - { - debug_stream << message; - ESP_LOGD(TAG, "sending message: %s", debug.data()); - }, - message); + debug_stream << message; + ESP_LOGD(TAG, "sending message: %s", debug.data()); } static void debug_print_received_message(const ApiMessage &message) { etl::string<128> debug; etl::string_stream debug_stream{debug}; - std::visit( - [&](const auto &message) -> void - { - debug_stream << message; - ESP_LOGD(TAG, "received message: %s", debug.data()); - }, - message); + debug_stream << message; + ESP_LOGD(TAG, "received message: %s", debug.data()); } -void Server::send_message(const AppMessage &message) +void Server::send_message( + const std::pair> &message) { ESP_LOGD( TAG, "%s called from task: %s", __FUNCTION__, pcTaskGetName(nullptr)); - if(errQUEUE_FULL == out_queue->send(&message, pdMS_TO_TICKS(100))) + if(queue_error::SUCCESS != out_queue->send(&message, pdMS_TO_TICKS(100))) { ESP_LOGE(TAG, "Failed to send message to websocket"); return; diff --git a/firmware/main/CMakeLists.txt b/firmware/main/CMakeLists.txt index 9a9dee69..d9e96b5b 100644 --- a/firmware/main/CMakeLists.txt +++ b/firmware/main/CMakeLists.txt @@ -1,54 +1,33 @@ -if(ESP_PLATFORM) - # FIXME: move the main_thread to a separate component so the build system can - # be used more effectively to keep it free from hardware dependencies - # It should link to no idf:: targets, or any espressif specific targets from - # shrapnel:: - idf_component_register(SRCS main.cpp - INCLUDE_DIRS include - REQUIRES - driver - esp_http_server - mdns - nvs_flash - LDFRAGMENTS esp-dsp.lf - ) +idf_component_register(SRCS main.cpp + REQUIRES + driver + esp_http_server + mdns + nvs_flash + LDFRAGMENTS esp-dsp.lf +) - target_link_libraries(${COMPONENT_LIB} - PRIVATE - shrapnel::abstract_dsp - shrapnel::audio_events - shrapnel::cmd_handling - shrapnel::console - shrapnel::esp_persistence - shrapnel::etl - shrapnel::hardware - shrapnel::heap_tracing - shrapnel::i2s - shrapnel::messages - shrapnel::midi_handling - shrapnel::midi_mapping - shrapnel::midi_protocol - shrapnel::os - shrapnel::pcm3060 - shrapnel::persistence - shrapnel::presets - shrapnel::server - shrapnel::wifi - shrapnel::compiler_warning_flags) -else() - add_library(main INTERFACE) - add_library(shrapnel::main ALIAS main) - - target_include_directories(main INTERFACE include) - target_link_libraries(main INTERFACE - shrapnel::audio_events - shrapnel::cmd_handling - shrapnel::etl - shrapnel::messages - shrapnel::midi_handling - shrapnel::midi_mapping - shrapnel::midi_protocol - shrapnel::os - shrapnel::persistence - shrapnel::presets) -endif() +target_link_libraries(${COMPONENT_LIB} + PRIVATE + shrapnel::abstract_dsp + shrapnel::audio_events + shrapnel::cmd_handling + shrapnel::console + shrapnel::esp_midi_uart + shrapnel::esp_persistence + shrapnel::etl + shrapnel::hardware + shrapnel::heap_tracing + shrapnel::i2s + shrapnel::main_thread + shrapnel::messages + shrapnel::midi_handling + shrapnel::midi_mapping + shrapnel::midi_protocol + shrapnel::os + shrapnel::pcm3060 + shrapnel::persistence + shrapnel::presets + shrapnel::server + shrapnel::wifi + shrapnel::compiler_warning_flags) diff --git a/firmware/main/main.cpp b/firmware/main/main.cpp index 6a0127e3..f5892104 100644 --- a/firmware/main/main.cpp +++ b/firmware/main/main.cpp @@ -57,8 +57,8 @@ #include "audio_events.h" #include "audio_param.h" -#include "cmd_handling.h" #include "esp_crud.h" +#include "esp_midi_uart.h" #include "esp_persistence.h" #include "hardware.h" #include "i2s.h" @@ -86,6 +86,83 @@ namespace shrapnel { using Crud = persistence::EspCrud<256>; +template +class ParameterObserver final : public parameters::ParameterObserver +{ +public: + explicit ParameterObserver( + std::shared_ptr a_persistence) + : is_save_throttled{true}, + persistence{std::move(a_persistence)}, + timer{"param save throttle", + os::ms_to_ticks(10'000), + false, + etl::delegate::create< + ParameterObserver, + &ParameterObserver::timer_callback>(*this)} + { + } + + void + notification(std::pair parameter) override + { + auto &[id, value] = parameter; + ESP_LOGI( + TAG, "notified about parameter change %s %f", id.data(), value); + if(!updated_parameters.available()) + { + ESP_LOGE(TAG, "no space available"); + return; + } + + updated_parameters[id] = value; + + if(!timer.is_active()) + { + if(os::timer_error::TIMER_START_SUCCESS != + timer.start(os::ms_to_ticks(5))) + { + ESP_LOGE(TAG, "Failed to start parameter observer timer"); + } + } + } + + void loop() + { + if(!is_save_throttled.test_and_set()) + { + persist_parameters(); + ESP_LOGI(TAG, "Parameters saved to NVS"); + } + } + +private: + void timer_callback() + { + is_save_throttled.clear(); + is_save_throttled.notify_all(); + } + + void persist_parameters() + { + for(const auto ¶m : updated_parameters) + { + auto rc = persistence->save(param.first.data(), param.second); + if(rc != 0) + { + ESP_LOGE(TAG, "failed to save parameters %d", rc); + } + } + + updated_parameters.clear(); + } + + std::atomic_flag is_save_throttled; + std::shared_ptr persistence; + os::Timer timer; + etl::map updated_parameters; +}; + extern "C" { static void disconnect_handler(void *arg, @@ -112,8 +189,8 @@ static void disconnect_handler(void *arg, ESP_LOGI(TAG, "WiFi disconnected"); auto queue{reinterpret_cast(arg)}; wifi::InternalEvent event{wifi::InternalEvent::DISCONNECT}; - int rc{queue->send(&event, 0)}; - if(rc != pdPASS) + auto rc{queue->send(&event, 0)}; + if(rc != queue_error::SUCCESS) { ESP_LOGE(TAG, "Failed to send disconnect event to queue"); } @@ -131,8 +208,8 @@ static void connect_handler(void *arg, ESP_LOGI(TAG, "WiFi connected"); auto queue{reinterpret_cast(arg)}; wifi::InternalEvent event{wifi::InternalEvent::CONNECT_SUCCESS}; - int rc{queue->send(&event, 0)}; - if(rc != pdPASS) + auto rc{queue->send(&event, 0)}; + if(rc != queue_error::SUCCESS) { ESP_LOGE(TAG, "Failed to send connect event to queue"); } @@ -149,8 +226,8 @@ static void wifi_start_handler(void *arg, auto queue{reinterpret_cast(arg)}; wifi::InternalEvent event{wifi::InternalEvent::STARTED}; - int rc{queue->send(&event, 0)}; - if(rc != pdPASS) + auto rc{queue->send(&event, 0)}; + if(rc != queue_error::SUCCESS) { ESP_LOGE(TAG, "Failed to send start event to queue"); } @@ -247,7 +324,63 @@ extern "C" void app_main(void) ESP_ERROR_CHECK(esp_event_loop_create_default()); auto persistence = std::make_shared(); - auto audio_params = std::make_shared(); + auto audio_params = std::make_shared>(); + + auto create_and_load_parameter = [&](const parameters::id_t &name, + float minimum, + float maximum, + float default_value) + { + std::optional loaded_value; + float value; + int rc = persistence->load(name.data(), value); + if(rc != 0) + { + ESP_LOGW(TAG, "Parameter %s failed to load", name.data()); + goto out; + } + + loaded_value = value; + out: + auto range = maximum - minimum; + rc = audio_params->create_and_add_parameter( + name, + minimum, + maximum, + loaded_value.has_value() ? *loaded_value * range + minimum + : default_value); + if(rc != 0) + { + ESP_LOGE(TAG, "Failed to create parameter %s", name.c_str()); + } + }; + + // XXX: These are duplicated in the JUCE plugin, be sure to update both at + // the same time + create_and_load_parameter("ampGain", 0, 1, 0.5); + create_and_load_parameter("ampChannel", 0, 1, 0); + create_and_load_parameter("bass", 0, 1, 0.5); + create_and_load_parameter("middle", 0, 1, 0.5); + create_and_load_parameter("treble", 0, 1, 0.5); + //contour gets unstable when set to 0 + create_and_load_parameter("contour", 0.01, 1, 0.5); + create_and_load_parameter("volume", -30, 0, -15); + + create_and_load_parameter("noiseGateThreshold", -80, 0, -60); + create_and_load_parameter("noiseGateHysteresis", 0, 5, 0); + create_and_load_parameter("noiseGateAttack", 1, 50, 10); + create_and_load_parameter("noiseGateHold", 1, 250, 50); + create_and_load_parameter("noiseGateRelease", 1, 250, 50); + create_and_load_parameter("noiseGateBypass", 0, 1, 0); + + create_and_load_parameter("chorusRate", 0.1, 4, 0.95); + create_and_load_parameter("chorusDepth", 0, 1, 0.3); + create_and_load_parameter("chorusMix", 0, 1, 0.8); + create_and_load_parameter("chorusBypass", 0, 1, 1); + + create_and_load_parameter("wahPosition", 0, 1, 0.5); + create_and_load_parameter("wahVocal", 0, 1, 0); + create_and_load_parameter("wahBypass", 0, 1, 1); i2c_setup(); profiling_init(DMA_BUF_SIZE, SAMPLE_RATE); @@ -322,15 +455,113 @@ extern "C" void app_main(void) auto wifi_send_event = [&](wifi::InternalEvent event) { auto rc = wifi_queue.send(&event, 0); - if(rc != pdPASS) + if(rc != queue_error::SUCCESS) { ESP_LOGE(TAG, "Failed to post wifi event to queue"); } }; +#if 0 + auto convert_to = + [](const AppMessage &message) -> std::pair + { + return std::visit( + overloaded{ + [](const parameters::ApiMessage &message) { + return std::pair{message, std::nullopt}; + }, + [](const midi::MappingApiMessage &message) { + return std::pair{message, std::nullopt}; + }, + [](const events::ApiMessage &message) { + return std::pair{message, std::nullopt}; + }, + [](const selected_preset::SelectedPresetApiMessage &message) { + return std::pair{message, std::nullopt}; + }, + [](const presets::PresetsApiMessage &message) { + return std::pair{message, std::nullopt}; + }, + [](const midi::Message &message) { + return std::pair{message, std::nullopt}; + }, + [](const ParameterUpdateMessage &message) + { + return std::visit( + overloaded{ + [](const ParameterUpdateApi &message) { + return std::pair{ + message.update, std::nullopt}; + }, + [](const ParameterUpdateHost &message) { + return std::pair{ + message.update, std::nullopt}; + }, + [](const ParameterUpdateOther &message) { + return std::pair{ + message.update, std::nullopt}; + }, + }, + message); + }, + }, + message); + }; +#endif + auto in_queue = new Queue; - auto out_queue = new Queue; - auto server = new Server(in_queue, out_queue); + auto out_queue = + new Queue>, QUEUE_LEN>; + + auto convert_from = + [](const std::pair &message) -> AppMessage + { + int fd = message.second; + + return std::visit( + overloaded{ + [=](const parameters::ApiMessage &message) + { + return std::visit( + overloaded{ + [](const parameters::Initialise &message) + { return AppMessage{message}; }, + [=](const parameters::Update &message) + { + return AppMessage{ParameterUpdateApi{ + .update{message}, + .fd{fd}, + }}; + }, + }, + message); + }, + [](const midi::MappingApiMessage &message) + { return AppMessage{message}; }, + [](const events::ApiMessage &message) + { return AppMessage{message}; }, + [](const selected_preset::SelectedPresetApiMessage &message) + { return AppMessage{message}; }, + [](const presets::PresetsApiMessage &message) + { return AppMessage{message}; }, + [](const midi::Message &message) + { return AppMessage{message}; }, + }, + message.first); + }; + + auto server_send_output = [&](const std::pair &in, + uint32_t time_to_wait) -> void + { + auto message = convert_from(in); + auto rc = in_queue->send(&message, time_to_wait); + if(rc != queue_error::SUCCESS) + { + ESP_LOGE(TAG, "in_queue message dropped"); + } + }; + + auto server = new Server(server_send_output, out_queue); auto app_send_event = [&](wifi::UserEvent event) { @@ -377,26 +608,38 @@ extern "C" void app_main(void) debug_dump_task_list(); - auto send_message = [&](const AppMessage &message) - { server->send_message(message); }; + auto send_message = [&](const ApiMessage &message) { + server->send_message({message, std::nullopt}); + }; + + auto send_message2 = [&](const ApiMessage &message, + const std::optional fd) { + server->send_message({message, fd}); + }; + + auto get_midi_byte = [&]() -> std::optional + { return midi_uart->get_byte(0); }; - auto main_thread = MainThread( - send_message, - *in_queue, - midi_uart, - audio_params, - persistence, - std::make_unique("nvs", "midi_mapping"), - std::make_unique("nvs", "presets")); + auto main_thread = + MainThread>( + send_message, + send_message2, + *in_queue, + audio_params, + persistence, + std::make_unique("nvs", "midi_mapping"), + std::make_unique("nvs", "presets"), + get_midi_byte); auto send_midi_message = [&](const midi::Message &message) { - auto app_message = AppMessage{ApiMessage{message}, std::nullopt}; - int rc = in_queue->send(&app_message, portMAX_DELAY); + auto app_message = AppMessage{message}; + auto rc = in_queue->send(&app_message, portMAX_DELAY); - if(rc != pdPASS) + if(rc != queue_error::SUCCESS) { - ESP_LOGE(TAG, "Failed to send to main queue %d", rc); + ESP_LOGE( + TAG, "Failed to send to main queue %d", static_cast(rc)); } }; @@ -437,6 +680,9 @@ extern "C" void app_main(void) audio::i2s_setup(PROFILING_GPIO, audio_params.get()); + ParameterObserver<20> parameter_observer{persistence}; + audio_params->add_observer(parameter_observer); + ESP_LOGI(TAG, "setup done"); ESP_LOGI(TAG, "stack: %d", uxTaskGetStackHighWaterMark(NULL)); heap_caps_print_heap_info(MALLOC_CAP_DEFAULT); @@ -448,6 +694,8 @@ extern "C" void app_main(void) main_thread.loop(); + parameter_observer.loop(); + { // i2s produces an event on each buffer TX/RX. We process all the // events in the current iteration, so that the queue doesn't fill @@ -460,7 +708,7 @@ extern "C" void app_main(void) } wifi::InternalEvent wifi_event; - while(pdPASS == wifi_queue.receive(&wifi_event, 0)) + while(queue_error::SUCCESS == wifi_queue.receive(&wifi_event, 0)) { wifi::State state{wifi_state_chart.get_state_id()}; ESP_LOGI( diff --git a/frontend/test/firmware_api_test/util.dart b/frontend/test/firmware_api_test/util.dart index 7f8c7833..e414b016 100644 --- a/frontend/test/firmware_api_test/util.dart +++ b/frontend/test/firmware_api_test/util.dart @@ -332,7 +332,16 @@ class ShrapnelUart { .cast>() .transform(utf8.decoder) .transform(const LineSplitter()) - .asBroadcastStream(); + // Sometimes a log line fails to decode, possibly due to UART data + // corruption or because the firmware prints random binary data. Prevent + // it from causing a failing test. + .handleError((Object error, StackTrace stackTrace) { + _logger.severe( + 'Failed to decode logs, possible data corruption', + error, + stackTrace, + ); + }, test: (error) => error is FormatException).asBroadcastStream(); Stream get log => _log; diff --git a/plugins/shrapnel/.idea/.gitignore b/plugins/shrapnel/.idea/.gitignore deleted file mode 100644 index 13566b81..00000000 --- a/plugins/shrapnel/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/plugins/shrapnel/.idea/.name b/plugins/shrapnel/.idea/.name deleted file mode 100644 index 0f71c1c8..00000000 --- a/plugins/shrapnel/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -ShrapnelDSP \ No newline at end of file diff --git a/plugins/shrapnel/.idea/cmake.xml b/plugins/shrapnel/.idea/cmake.xml deleted file mode 100644 index 2ad2554e..00000000 --- a/plugins/shrapnel/.idea/cmake.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/plugins/shrapnel/.idea/codeStyles/codeStyleConfig.xml b/plugins/shrapnel/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index a55e7a17..00000000 --- a/plugins/shrapnel/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/plugins/shrapnel/.idea/discord.xml b/plugins/shrapnel/.idea/discord.xml deleted file mode 100644 index 30bab2ab..00000000 --- a/plugins/shrapnel/.idea/discord.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/plugins/shrapnel/.idea/misc.xml b/plugins/shrapnel/.idea/misc.xml deleted file mode 100644 index 392db40a..00000000 --- a/plugins/shrapnel/.idea/misc.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/plugins/shrapnel/.idea/modules.xml b/plugins/shrapnel/.idea/modules.xml deleted file mode 100644 index 3561054b..00000000 --- a/plugins/shrapnel/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/plugins/shrapnel/.idea/shrapnel.iml b/plugins/shrapnel/.idea/shrapnel.iml deleted file mode 100644 index f08604bb..00000000 --- a/plugins/shrapnel/.idea/shrapnel.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/plugins/shrapnel/.idea/vcs.xml b/plugins/shrapnel/.idea/vcs.xml deleted file mode 100644 index b2bdec2d..00000000 --- a/plugins/shrapnel/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/plugins/shrapnel/CMakeLists.txt b/plugins/shrapnel/CMakeLists.txt index 874ed480..687c04bf 100644 --- a/plugins/shrapnel/CMakeLists.txt +++ b/plugins/shrapnel/CMakeLists.txt @@ -7,20 +7,20 @@ set(CMAKE_CXX_STANDARD_REQUIRED TRUE) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -add_subdirectory(../../effects/abstract_dsp abstract_dsp) -add_subdirectory(../../effects/chorus chorus) -add_subdirectory(../../effects/gate gate) -add_subdirectory(../../effects/valvestate valvestate) -add_subdirectory(../../effects/wah wah) -add_subdirectory(../../firmware/components/audio audio) -add_subdirectory(../../firmware/components/compiler_warning_flags compiler_warning_flags) +add_subdirectory(../../effects effects) +add_subdirectory(../../shrapnel shrapnel) +add_subdirectory(../../thirdparty/JUCE JUCE) +add_subdirectory(../../thirdparty/uWebSockets uWebSockets) +add_subdirectory(../JUCE_static JUCE_static) +add_subdirectory(../../thirdparty/esp-idf-components/nanopb nanopb) +add_subdirectory(../../thirdparty/esp-idf-components/etl etl) + +# Fake esp-idf components are required for esp-dsp to build add_subdirectory(../../test/support/esp_common esp_common) add_subdirectory(../../test/support/esp_hw_support esp_hw_support) add_subdirectory(../../test/support/log log) -add_subdirectory(../../thirdparty/JUCE JUCE) add_subdirectory(../../thirdparty/esp-idf-components/esp-dsp esp-dsp) add_subdirectory(../../thirdparty/esp-idf-components/esp32-fft esp32-fft) -add_subdirectory(../JUCE_static JUCE_static) juce_add_plugin(ShrapnelDSP COMPANY_NAME Barabas Raffai @@ -31,9 +31,12 @@ juce_add_plugin(ShrapnelDSP target_sources(ShrapnelDSP PRIVATE - PluginProcessor.cpp) + PluginProcessor.cpp + server.cpp) target_link_libraries(ShrapnelDSP PRIVATE - audio + shrapnel::audio + shrapnel::main_thread + shrapnel::uwebsockets juce_modules) diff --git a/plugins/shrapnel/PluginProcessor.cpp b/plugins/shrapnel/PluginProcessor.cpp index 62ce8d5a..312f7aaf 100644 --- a/plugins/shrapnel/PluginProcessor.cpp +++ b/plugins/shrapnel/PluginProcessor.cpp @@ -81,6 +81,9 @@ AudioPluginAudioProcessor::AudioPluginAudioProcessor() "wahBypass", "Wah Bypass", 0.f, 1.f, 1.f), }, }, + parameter_adapter{std::make_shared>>(parameters)}, + main_thread{parameter_adapter}, processor{ // clang-format off ShrapnelAudioProcessor{ @@ -118,6 +121,8 @@ AudioPluginAudioProcessor::AudioPluginAudioProcessor() // clang-format on } { + bool success = main_thread.startThread(); + jassert(success); } AudioPluginAudioProcessor::~AudioPluginAudioProcessor() = default; @@ -203,7 +208,7 @@ bool AudioPluginAudioProcessor::isBusesLayoutSupported( if(layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) return false; - // This checks if the input layout matches the output layout + // This checks if the input layout matches the output layout #if !JucePlugin_IsSynth if(layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) return false; diff --git a/plugins/shrapnel/PluginProcessor.h b/plugins/shrapnel/PluginProcessor.h index edf364ae..612c171c 100644 --- a/plugins/shrapnel/PluginProcessor.h +++ b/plugins/shrapnel/PluginProcessor.h @@ -19,6 +19,7 @@ #pragma once +#include "plugin_main_thread.h" #include "shrapnel_processor.h" #include @@ -57,8 +58,15 @@ class AudioPluginAudioProcessor : public juce::AudioProcessor private: juce::AudioProcessorValueTreeState parameters; + std::shared_ptr>> + parameter_adapter; + shrapnel::PluginMainThread> + main_thread; + shrapnel::dsp::BlockProcessor processor; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AudioPluginAudioProcessor) }; diff --git a/plugins/shrapnel/parameter_adapter.h b/plugins/shrapnel/parameter_adapter.h new file mode 100644 index 00000000..7d9079be --- /dev/null +++ b/plugins/shrapnel/parameter_adapter.h @@ -0,0 +1,83 @@ +#include "main_thread.h" +#include + +/** Adapts a JUCE AudioProcessorValueTreeState to work with shrapnel main thread + * + */ + +namespace shrapnel { + +template + requires UpdatableParameter && + GettableParameter +class ParameterAdapter : private juce::AudioProcessorValueTreeState::Listener +{ + static constexpr char TAG[] = "ParameterAdapter"; + +public: + explicit ParameterAdapter(juce::AudioProcessorValueTreeState &a_parameters) + : apvts{a_parameters} + { + apvts.addParameterListener("ampGain", this); + apvts.addParameterListener("ampChannel", this); + apvts.addParameterListener("bass", this); + apvts.addParameterListener("middle", this); + apvts.addParameterListener("treble", this); + apvts.addParameterListener("contour", this); + apvts.addParameterListener("volume", this); + apvts.addParameterListener("noiseGateThreshold", this); + apvts.addParameterListener("noiseGateHysteresis", this); + apvts.addParameterListener("noiseGateAttack", this); + apvts.addParameterListener("noiseGateHold", this); + apvts.addParameterListener("noiseGateRelease", this); + apvts.addParameterListener("noiseGateBypass", this); + apvts.addParameterListener("chorusRate", this); + apvts.addParameterListener("chorusDepth", this); + apvts.addParameterListener("chorusMix", this); + apvts.addParameterListener("chorusBypass", this); + apvts.addParameterListener("wahPosition", this); + apvts.addParameterListener("wahVocal", this); + apvts.addParameterListener("wahBypass", this); + } + + int update(const parameters::id_t &id, float value) + { + ESP_LOGD(TAG, "update %s %f", id.c_str(), value); + + // TODO: + // Note that to make sure the host correctly handles automation, you should call + // the beginChangeGesture() and endChangeGesture() methods to tell the host when + // the user has started and stopped changing the parameter. + // + // This is not implemented in the GUI at the moment + auto parameter = apvts.getParameter(id.data()); + if(parameter == nullptr) + { + return -1; + } + + // It should be safe to call this from a background thread: + // https://forum.juce.com/t/calling-setvaluenotifyinghost-from-processblock/26073/9 + + // TODO this may expect a value between 0 and 1 like getValue returns + parameter->setValueNotifyingHost(parameter->convertFrom0to1(value)); + return 0; + } + +private: + void parameterChanged(const juce::String &id, float value) override + { + // FIXME: what thread is calling this? Probably need to send a message + // and handle on the main thread. + auto id_str = id.toStdString(); + ESP_LOGD(TAG, "parameterChanged %s %f", id_str.c_str(), value); + + on_parameter_updated_by_host(id_str.c_str(), value); + } + + juce::AudioProcessorValueTreeState &apvts; + etl::delegate + on_parameter_updated_by_host; +}; + +} // namespace shrapnel diff --git a/plugins/shrapnel/plugin_main_thread.h b/plugins/shrapnel/plugin_main_thread.h new file mode 100644 index 00000000..1dd88aaf --- /dev/null +++ b/plugins/shrapnel/plugin_main_thread.h @@ -0,0 +1,408 @@ +/* + * Copyright 2022 Barabas Raffai + * + * This file is part of ShrapnelDSP. + * + * ShrapnelDSP is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * ShrapnelDSP is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * ShrapnelDSP. If not, see . + */ + +#pragma once + +#include "main_thread.h" +#include "parameter_adapter.h" +#include + +#include "juce_core/juce_core.h" +#include "server.h" +#include + +// TODO: +// server + +// TODO does it really make sense to save the parameters as a VST plugin? That +// is the host's responsibility. +// +// We can keep saving presets, and that requires reading from parameters, but +// maybe the parameter storage code from the firmware should be removed from the +// main thread, and instead a wrapper on AudioProcessorValueTreeState should be +// injected. + +namespace shrapnel { + +// TODO this is probably unsound. The juce::Timer internal to the PropertiesFile +// will run the callback on some random thread. It locks the PropertiesFile +// lock, but setValue etc. are inherited from the PropertySet and do not lock. +// We need to lock it ourselves. +class JuceCrud final : public shrapnel::persistence::Crud> +{ +public: + explicit JuceCrud(const std::string &name) : options{} + { + options.applicationName = name; + options.filenameSuffix = "properties"; + options.folderName = "ShrapnelDSP"; + options.osxLibrarySubFolder = "Application Support"; + options.commonToAllUsers = false; + options.ignoreCaseOfKeyNames = false; + options.doNotSave = false; + options.millisecondsBeforeSaving = 1000; + options.storageFormat = juce::PropertiesFile::storeAsXML; + options.processLock = nullptr; + + propertiesFile = std::make_unique(options); + }; + + int create(const std::span &data, uint32_t &id_out) override + { + // start at 0 in case last ID has not been saved to disk + auto id = + static_cast(propertiesFile->getIntValue(last_id_key, 0)); + id++; + id_out = id; + + auto data_base64 = juce::Base64::toBase64(data.data(), data.size()); + propertiesFile->setValue(id_to_key(id), juce::var(data_base64)); + propertiesFile->setValue(last_id_key, juce::var(static_cast(id))); + + return 0; + } + + int read(uint32_t id, std::span &data_out) override + { + return _read(id_to_key(id), data_out); + } + + int update(uint32_t id, const std::span &data) override + { + auto data_base64 = juce::Base64::toBase64(data.data(), data.size()); + propertiesFile->setValue(id_to_key(id), juce::var(data_base64)); + + return 0; + } + + int destroy(uint32_t id) override + { + propertiesFile->removeValue(id_to_key(id)); + + return 0; + } + + void for_each(etl::delegate &)> + callback) override + { + auto all_keys = propertiesFile->getAllProperties().getAllKeys(); + + for(const auto &key : all_keys) + { + std::array memory; + std::span buffer{memory}; + + auto rc = _read(key, buffer); + if(rc == 0) + { + auto id = key_to_id(key); + callback(id, buffer); + } + } + } + +private: + static juce::String id_to_key(uint32_t id) + { + char hex[9]; + int rc = snprintf(hex, sizeof hex, "%08" PRIx32, id); + assert(rc == 8); + return {hex, 9}; + }; + + static uint32_t key_to_id(const juce::String &key) + { + id_t id; + int rc = sscanf(key.getCharPointer(), "%08" PRIx32, &id); + assert(rc == 1); + return id; + }; + + int _read(juce::StringRef id, std::span &data_out) + { + auto value = propertiesFile->getValue(id); + if(value.isEmpty()) + return -1; + + juce::MemoryOutputStream stream; + + auto success = juce::Base64::convertFromBase64(stream, value); + if(!success) + { + return -1; + } + + if(stream.getDataSize() > data_out.size()) + { + return -1; + } + + std::copy(stream.getMemoryBlock().begin(), + stream.getMemoryBlock().end(), + data_out.begin()); + data_out = data_out.subspan(stream.getDataSize()); + + return 0; + } + + static constexpr char last_id_key[] = "last_id"; + + juce::PropertiesFile::Options options; + std::unique_ptr propertiesFile; +}; + +class JuceStorage final : public shrapnel::persistence::Storage +{ +public: + explicit JuceStorage() : options{} + { + options.applicationName = "storage"; + options.filenameSuffix = "properties"; + options.folderName = "ShrapnelDSP"; + options.osxLibrarySubFolder = "Application Support"; + options.commonToAllUsers = false; + options.ignoreCaseOfKeyNames = false; + options.doNotSave = false; + options.millisecondsBeforeSaving = 1000; + options.storageFormat = juce::PropertiesFile::storeAsXML; + options.processLock = nullptr; + + propertiesFile = std::make_unique(options); + }; + + int save(const char *key, std::span data) override + { + auto data_base64 = juce::Base64::toBase64(data.data(), data.size()); + propertiesFile->setValue(key, data_base64); + return 0; + } + + int save(const char *key, etl::string_view data) override + { + propertiesFile->setValue(key, data.data()); + return 0; + } + + int save(const char *key, uint32_t data) override + { + propertiesFile->setValue(key, static_cast(data)); + return 0; + } + + int save(const char *key, float data) override + { + propertiesFile->setValue(key, data); + return 0; + } + + int load(const char *key, std::span &data) override + { + if(!propertiesFile->containsKey(key)) + { + return -1; + } + + auto value = propertiesFile->getValue(key); + + juce::MemoryOutputStream stream; + + auto success = juce::Base64::convertFromBase64(stream, value); + if(!success) + { + return -1; + } + + if(stream.getDataSize() > data.size()) + { + return -1; + } + + std::copy(stream.getMemoryBlock().begin(), + stream.getMemoryBlock().end(), + data.begin()); + data = data.subspan(stream.getDataSize()); + return 0; + } + + int load(const char *key, etl::istring &data) override + { + if(!propertiesFile->containsKey(key)) + { + return -1; + } + + auto value = propertiesFile->getValue(key); + auto required_size = value.getNumBytesAsUTF8(); + + data.initialize_free_space(); + data.uninitialized_resize(required_size); + + if(data.size() != required_size) + { + return -1; + } + + value.copyToUTF8(data.data(), data.max_size()); + return 0; + } + + int load(const char *key, uint32_t &data) override + { + if(!propertiesFile->containsKey(key)) + { + return -1; + } + + data = static_cast(propertiesFile->getIntValue(key)); + return 0; + } + + int load(const char *key, float &data) override + { + if(!propertiesFile->containsKey(key)) + { + return -1; + } + + data = static_cast(propertiesFile->getDoubleValue(key)); + return 0; + } + +private: + juce::PropertiesFile::Options options; + std::unique_ptr propertiesFile; +}; + +template +class PluginMainThread final : public juce::Thread +{ +public: + explicit PluginMainThread( + std::shared_ptr> parameters) + : juce::Thread{"shrapnel"}, + server{etl::delegate &in, + uint32_t time_to_wait)>:: + create(*this)}, + main_thread{ + [&](const ApiMessage &message) { + server.send_message({message, std::nullopt}); + }, + [&](const ApiMessage &message, const std::optional fd) { + server.send_message({message, fd}); + }, + in_queue, + std::move(parameters), + std::make_shared(), + std::make_unique("midi_mapping"), + std::make_unique("presets"), + []() -> std::optional + { + // FIXME: connect to JUCE MIDI stream + return std::nullopt; + }} + { + server.start(); + } + + void run() override + { + juce::Logger::writeToLog("Main thread started"); + + while(!threadShouldExit()) + { + wait(10); + + main_thread.loop(); + } + }; + +private: + // A queue filled by the server with received API messages + shrapnel::Queue in_queue; + Server server; + std::shared_ptr audio_params; + shrapnel::MainThread<4, AudioParametersT> main_thread; + + static AppMessage convert_from(const std::pair &message) + { + int fd = message.second; + + return std::visit( + overloaded{ + [=](const parameters::ApiMessage &message) + { + return std::visit( + overloaded{ + [](const parameters::Initialise &message) + { return AppMessage{message}; }, + [=](const parameters::Update &message) + { + return AppMessage{ParameterUpdateApi{ + .update{message}, + .fd{fd}, + }}; + }, + }, + message); + }, + [](const midi::MappingApiMessage &message) + { return AppMessage{message}; }, + [](const events::ApiMessage &message) + { return AppMessage{message}; }, + [](const selected_preset::SelectedPresetApiMessage &message) + { return AppMessage{message}; }, + [](const presets::PresetsApiMessage &message) + { return AppMessage{message}; }, + [](const midi::Message &message) + { return AppMessage{message}; }, + }, + message.first); + }; + + void server_send_output(const std::pair &in, + uint32_t time_to_wait) + { + auto message = convert_from(in); + auto rc = in_queue.send(&message, time_to_wait); + if(rc != queue_error::SUCCESS) + { + ESP_LOGE(TAG, "in_queue message dropped"); + } + }; + + void parameters_send_host_update(const char *id, float value) + { + auto message = AppMessage{ + ParameterUpdateHost{ + .update{ + .id{id}, + .value{value}, + }, + }, + }; + auto rc = in_queue.send(&message, 100); + if(rc != queue_error::SUCCESS) + { + ESP_LOGE(TAG, "in_queue message dropped"); + } + }; +}; + +} // namespace shrapnel diff --git a/plugins/shrapnel/server.cpp b/plugins/shrapnel/server.cpp new file mode 100644 index 00000000..8575e493 --- /dev/null +++ b/plugins/shrapnel/server.cpp @@ -0,0 +1,247 @@ +#include "server.h" +#include "App.h" +#include + +#define TAG "server" + +namespace shrapnel { + +static void debug_print_sent_message(const ApiMessage &message); +static void debug_print_received_message(const ApiMessage &message); +static void send_websocket_message( + const std::pair> &message); + +uWS::App *globalApp; +uWS::Loop *globalLoop; + +struct UserData +{ +}; + +Server::Server(etl::delegate &in, + uint32_t time_to_wait)> a_output_message) + : Thread("server"), + output_message{a_output_message} +{ +} + +void Server::start() { startThread(); } + +void Server::send_message( + const std::pair> &message) +{ + if(!message.second.has_value()) + { + ESP_LOGD(TAG, "%s source fd is null", __FUNCTION__); + } + else + { + ESP_LOGD(TAG, "%s source fd = %d", __FUNCTION__, *message.second); + } + + debug_print_sent_message(message.first); + + send_websocket_message(message); +} + +void send_websocket_message( + const std::pair> &message) +{ + std::array memory{}; + auto buffer = std::span{memory}; + + auto encoded = api::to_bytes(message.first, buffer); + if(!encoded.has_value()) + { + ESP_LOGE(TAG, "Failed to encode message"); + return; + } + + ESP_LOGD(TAG, "%s len = %zd", __FUNCTION__, encoded->size()); +#if 0 + // TODO + ESP_LOG_BUFFER_HEXDUMP( + TAG, encoded->data(), encoded->size(), ESP_LOG_VERBOSE); +#endif + + // TODO do not send it to any clients where the fd matches the passed in fd, + // or remove that feature. Maybe put a client ID into the message itself, so + // the frontend can ignore it. + + // Run the publish in the server thread + globalLoop->defer( + [=]() + { + globalApp->publish( + "broadcast", + std::string_view{(char *)encoded->data(), encoded->size()}, + uWS::OpCode::BINARY); + }); +} + +void Server::run() +{ + // TODO monitor threadShouldExit and stop when required + + auto app = + uWS::App() + .ws( + "/websocket", + { + .compression = uWS::CompressOptions::DISABLED, + .maxPayloadLength = 100 * 1024 * 1024, + .idleTimeout = 16, + .maxBackpressure = 100 * 1024 * 1024, + .closeOnBackpressureLimit = false, + .resetIdleTimeoutOnSend = false, + .sendPingsAutomatically = true, + .upgrade = + [](auto *res, auto *req, auto *webSocketContext) + { + /* Default handler copied from library */ + std::cout << "upgrade" << std::endl; + + std::string_view secWebSocketKey = + req->getHeader("sec-websocket-key"); + + std::string_view secWebSocketProtocol = + req->getHeader("sec-websocket-protocol"); + std::string_view secWebSocketExtensions = + req->getHeader("sec-websocket-extensions"); + + /* Safari 15 hack */ + if(uWS::hasBrokenCompression( + req->getHeader("user-agent"))) + { + secWebSocketExtensions = ""; + } + + res->template upgrade({}, + secWebSocketKey, + secWebSocketProtocol, + secWebSocketExtensions, + webSocketContext); + }, + + .open = + [](auto * /*ws*/) + { + /* Open event here, you may access ws->getUserData() which points to a PerSocketData struct */ + std::cout << "open" << std::endl; + }, + .message = + [this](uWS::WebSocket *ws, + std::string_view message, + uWS::OpCode opCode) + { + std::cout << "message" << std::endl; + + // decode + { + /* We should never see any of these packets */ + assert(opCode != uWS::OpCode::CONTINUATION); + assert(opCode != uWS::OpCode::TEXT); + assert(opCode != uWS::OpCode::CLOSE); + assert(opCode != uWS::OpCode::PING); + assert(opCode != uWS::OpCode::PONG); + + auto fd = static_cast( + reinterpret_cast( + ws->getNativeHandle())); + + ESP_LOGD(TAG, + "%s len = %zd", + __FUNCTION__, + message.size()); +#if 0 + // TODO implement hexdump + ESP_LOG_BUFFER_HEXDUMP(TAG, + message.data(), + message.size(), + ESP_LOG_VERBOSE); +#endif + + auto decoded = api::from_bytes( + {reinterpret_cast( + message.data()), + message.size()}); + if(decoded.has_value()) + { + debug_print_received_message(*decoded); + auto out = + std::pair{*decoded, fd}; + output_message(out, 100); + } + else + { + ESP_LOGE(TAG, + "failed to parse received message"); +#if 0 + // TODO implement hexdump + ESP_LOG_BUFFER_HEXDUMP(TAG, + message.data(), + message.size(), + ESP_LOG_ERROR); +#endif + } + } + + // print + }, + .dropped = + [](auto * /*ws*/, + std::string_view /*message*/, + uWS::OpCode /*opCode*/) + { + /* A message was dropped due to set maxBackpressure and closeOnBackpressureLimit limit */ + std::cout << "dropped" << std::endl; + }, + .drain = + [](auto * /*ws*/) + { + /* Check ws->getBufferedAmount() here */ + std::cout << "drain" << std::endl; + }, + .close = + [](auto * /*ws*/, + int /*code*/, + std::string_view /*message*/) + { + /* You may access ws->getUserData() here */ + std::cout << "close" << std::endl; + }, + }) + .listen(3000, + [](auto *listen_socket) + { + if(listen_socket) + { + std::cout << "Listening on port " << 3000 + << std::endl; + } + }); + + globalApp = &app; + globalLoop = uWS::Loop::get(); + app.run(); + + std::cout << "Failed to listen on port 3000" << std::endl; +} + +static void debug_print_sent_message(const ApiMessage &message) +{ + etl::string<128> debug; + etl::string_stream debug_stream{debug}; + debug_stream << message; + ESP_LOGD(TAG, "sending message: %s", debug.data()); +} + +static void debug_print_received_message(const ApiMessage &message) +{ + etl::string<128> debug; + etl::string_stream debug_stream{debug}; + debug_stream << message; + ESP_LOGD(TAG, "received message: %s", debug.data()); +} + +} // namespace shrapnel diff --git a/plugins/shrapnel/server.h b/plugins/shrapnel/server.h new file mode 100644 index 00000000..75e4cad1 --- /dev/null +++ b/plugins/shrapnel/server.h @@ -0,0 +1,25 @@ +#include "messages.h" +#include "os/queue.h" +#include + +namespace shrapnel { + +class Server final : private juce::Thread +{ +public: + Server(etl::delegate &in, + uint32_t time_to_wait)> output_message); + + void start(); + + void send_message(const std::pair> &message); + +private: + void run() override; + + etl::delegate &in, + uint32_t time_to_wait)> + output_message; +}; + +} // namespace shrapnel diff --git a/shrapnel/CMakeLists.txt b/shrapnel/CMakeLists.txt new file mode 100644 index 00000000..c4f741b3 --- /dev/null +++ b/shrapnel/CMakeLists.txt @@ -0,0 +1,15 @@ +add_subdirectory(api) +add_subdirectory(audio) +add_subdirectory(audio_events) +add_subdirectory(audio_param) +add_subdirectory(cmd_handling) +add_subdirectory(compiler_warning_flags) +add_subdirectory(float_convert) +add_subdirectory(messages) +add_subdirectory(main_thread) +add_subdirectory(midi_handling) +add_subdirectory(midi_mapping) +add_subdirectory(midi_protocol) +add_subdirectory(os) +add_subdirectory(persistence) +add_subdirectory(presets) diff --git a/firmware/components/api/CMakeLists.txt b/shrapnel/api/CMakeLists.txt similarity index 100% rename from firmware/components/api/CMakeLists.txt rename to shrapnel/api/CMakeLists.txt diff --git a/firmware/components/api/include/api.h b/shrapnel/api/include/api.h similarity index 100% rename from firmware/components/api/include/api.h rename to shrapnel/api/include/api.h diff --git a/firmware/components/audio/CMakeLists.txt b/shrapnel/audio/CMakeLists.txt similarity index 100% rename from firmware/components/audio/CMakeLists.txt rename to shrapnel/audio/CMakeLists.txt diff --git a/firmware/components/audio/include/audio_processor.h b/shrapnel/audio/include/audio_processor.h similarity index 100% rename from firmware/components/audio/include/audio_processor.h rename to shrapnel/audio/include/audio_processor.h diff --git a/firmware/components/audio/src/audio_processor.cpp b/shrapnel/audio/src/audio_processor.cpp similarity index 100% rename from firmware/components/audio/src/audio_processor.cpp rename to shrapnel/audio/src/audio_processor.cpp diff --git a/firmware/components/audio/src/speaker_coeffs.h b/shrapnel/audio/src/speaker_coeffs.h similarity index 100% rename from firmware/components/audio/src/speaker_coeffs.h rename to shrapnel/audio/src/speaker_coeffs.h diff --git a/firmware/components/audio_events/CMakeLists.txt b/shrapnel/audio_events/CMakeLists.txt similarity index 85% rename from firmware/components/audio_events/CMakeLists.txt rename to shrapnel/audio_events/CMakeLists.txt index d3690cdd..d9395327 100644 --- a/firmware/components/audio_events/CMakeLists.txt +++ b/shrapnel/audio_events/CMakeLists.txt @@ -1,11 +1,11 @@ -include(../../../thirdparty/esp-idf-components/nanopb/nanopb-generate-cpp.cmake) +include(../../thirdparty/esp-idf-components/nanopb/nanopb-generate-cpp.cmake) add_library(audio_events STATIC) add_library(shrapnel::audio_events ALIAS audio_events) set(NANOPB_GENERATE_CPP_APPEND_PATH ON) nanopb_generate_cpp(PROTO_SRCS PROTO_HDRS - ../../../proto/audio_events.proto) + ../../proto/audio_events.proto) target_include_directories(audio_events PUBLIC diff --git a/firmware/components/audio_events/include/audio_events.h b/shrapnel/audio_events/include/audio_events.h similarity index 100% rename from firmware/components/audio_events/include/audio_events.h rename to shrapnel/audio_events/include/audio_events.h diff --git a/firmware/components/audio_events/src/audio_events.cpp b/shrapnel/audio_events/src/audio_events.cpp similarity index 100% rename from firmware/components/audio_events/src/audio_events.cpp rename to shrapnel/audio_events/src/audio_events.cpp diff --git a/firmware/components/audio_param/CMakeLists.txt b/shrapnel/audio_param/CMakeLists.txt similarity index 100% rename from firmware/components/audio_param/CMakeLists.txt rename to shrapnel/audio_param/CMakeLists.txt diff --git a/firmware/components/audio_param/include/audio_param.h b/shrapnel/audio_param/include/audio_param.h similarity index 98% rename from firmware/components/audio_param/include/audio_param.h rename to shrapnel/audio_param/include/audio_param.h index e63b7df1..f1134959 100644 --- a/firmware/components/audio_param/include/audio_param.h +++ b/shrapnel/audio_param/include/audio_param.h @@ -57,6 +57,7 @@ class AudioParameterFloat id_t name; private: + static constexpr char TAG[] = "AudioParameterFloat"; std::atomic value; float minimum; float maximum; diff --git a/firmware/components/audio_param/src/audio_param.cpp b/shrapnel/audio_param/src/audio_param.cpp similarity index 89% rename from firmware/components/audio_param/src/audio_param.cpp rename to shrapnel/audio_param/src/audio_param.cpp index 805a149b..dbe43181 100644 --- a/firmware/components/audio_param/src/audio_param.cpp +++ b/shrapnel/audio_param/src/audio_param.cpp @@ -36,6 +36,8 @@ AudioParameterFloat::AudioParameterFloat(const id_t &a_name, void AudioParameterFloat::update(float a_value) { + ESP_LOGD(TAG, "update %s %f", name.c_str(), a_value); + if(a_value > 1) { return; @@ -53,7 +55,9 @@ void AudioParameterFloat::update(float a_value) float AudioParameterFloat::get(void) { auto range = maximum - minimum; - return (value - minimum) / range; + const float out_value = (value - minimum) / range; + ESP_LOGD(TAG, "get %s %f", name.c_str(), out_value); + return out_value; } std::atomic *AudioParameterFloat::get_raw_parameter(void) diff --git a/firmware/components/audio_param/test/test_audio_param.cpp b/shrapnel/audio_param/test/test_audio_param.cpp similarity index 100% rename from firmware/components/audio_param/test/test_audio_param.cpp rename to shrapnel/audio_param/test/test_audio_param.cpp diff --git a/firmware/components/cmd_handling/CMakeLists.txt b/shrapnel/cmd_handling/CMakeLists.txt similarity index 84% rename from firmware/components/cmd_handling/CMakeLists.txt rename to shrapnel/cmd_handling/CMakeLists.txt index e14a57e9..4d29ef43 100644 --- a/firmware/components/cmd_handling/CMakeLists.txt +++ b/shrapnel/cmd_handling/CMakeLists.txt @@ -1,16 +1,15 @@ -include(../../../thirdparty/esp-idf-components/nanopb/nanopb-generate-cpp.cmake) +include(../../thirdparty/esp-idf-components/nanopb/nanopb-generate-cpp.cmake) add_library(cmd_handling STATIC) add_library(shrapnel::cmd_handling ALIAS cmd_handling) set(NANOPB_GENERATE_CPP_APPEND_PATH ON) nanopb_generate_cpp(PROTO_SRCS PROTO_HDRS - ../../../proto/cmd_handling.proto) + ../../proto/cmd_handling.proto) target_sources(cmd_handling PRIVATE ${PROTO_SRCS} - src/cmd_handling.cpp src/cmd_handling_api.cpp) target_include_directories(cmd_handling @@ -21,9 +20,7 @@ target_include_directories(cmd_handling target_link_libraries(cmd_handling PUBLIC - idf::esp_common idf::log - idf::freertos shrapnel::api shrapnel::audio_param shrapnel::etl diff --git a/firmware/components/cmd_handling/include/cmd_handling_api.h b/shrapnel/cmd_handling/include/cmd_handling_api.h similarity index 100% rename from firmware/components/cmd_handling/include/cmd_handling_api.h rename to shrapnel/cmd_handling/include/cmd_handling_api.h diff --git a/firmware/components/cmd_handling/src/cmd_handling_api.cpp b/shrapnel/cmd_handling/src/cmd_handling_api.cpp similarity index 100% rename from firmware/components/cmd_handling/src/cmd_handling_api.cpp rename to shrapnel/cmd_handling/src/cmd_handling_api.cpp diff --git a/firmware/components/cmd_handling/test/test_cmd_handling.cpp b/shrapnel/cmd_handling/test/test_cmd_handling.cpp similarity index 99% rename from firmware/components/cmd_handling/test/test_cmd_handling.cpp rename to shrapnel/cmd_handling/test/test_cmd_handling.cpp index 3db3a6ea..310d11a0 100644 --- a/firmware/components/cmd_handling/test/test_cmd_handling.cpp +++ b/shrapnel/cmd_handling/test/test_cmd_handling.cpp @@ -17,6 +17,8 @@ * ShrapnelDSP. If not, see . */ +#if 0 + #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -161,3 +163,5 @@ TEST_F(CmdHandling, InitialiseParameters) } // namespace } // namespace + +#endif \ No newline at end of file diff --git a/firmware/components/compiler_warning_flags/CMakeLists.txt b/shrapnel/compiler_warning_flags/CMakeLists.txt similarity index 100% rename from firmware/components/compiler_warning_flags/CMakeLists.txt rename to shrapnel/compiler_warning_flags/CMakeLists.txt diff --git a/firmware/components/float_convert/CMakeLists.txt b/shrapnel/float_convert/CMakeLists.txt similarity index 100% rename from firmware/components/float_convert/CMakeLists.txt rename to shrapnel/float_convert/CMakeLists.txt diff --git a/firmware/components/float_convert/float_convert.h b/shrapnel/float_convert/float_convert.h similarity index 100% rename from firmware/components/float_convert/float_convert.h rename to shrapnel/float_convert/float_convert.h diff --git a/shrapnel/main_thread/CMakeLists.txt b/shrapnel/main_thread/CMakeLists.txt new file mode 100644 index 00000000..446b122d --- /dev/null +++ b/shrapnel/main_thread/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(shrapnel_main_thread INTERFACE) +add_library(shrapnel::main_thread ALIAS shrapnel_main_thread) + +target_include_directories(shrapnel_main_thread INTERFACE include) + +target_link_libraries(shrapnel_main_thread INTERFACE + shrapnel::midi_mapping + shrapnel::audio_param + shrapnel::cmd_handling + shrapnel::messages + shrapnel::midi_handling + shrapnel::midi_protocol + shrapnel::os + shrapnel::persistence + shrapnel::presets +) \ No newline at end of file diff --git a/firmware/main/include/main_thread.h b/shrapnel/main_thread/include/main_thread.h similarity index 59% rename from firmware/main/include/main_thread.h rename to shrapnel/main_thread/include/main_thread.h index e1adb4e5..8bfe403f 100644 --- a/firmware/main/include/main_thread.h +++ b/shrapnel/main_thread/include/main_thread.h @@ -20,10 +20,10 @@ #pragma once #include +#include #include #include "audio_param.h" -#include "cmd_handling.h" #include "messages.h" #include "midi_handling.h" #include "midi_mapping.pb.h" @@ -41,18 +41,32 @@ namespace { constexpr const char *TAG = "main_thread"; } // namespace -constexpr const size_t MAX_PARAMETERS = 20; +template +concept UpdatableParameter = requires(T a, parameters::id_t id, float value) { + { + a.update(id, value) + } -> std::same_as; +}; -class ParameterUpdateNotifier; +template +concept GettableParameter = requires(T a, parameters::id_t id) { + { + a.get(id) + } -> std::same_as; +}; -using AudioParameters = parameters::AudioParameters; -using SendMessageCallback = etl::delegate; +using SendMessageCallback = etl::delegate; +using SendMessageCallback2 = + etl::delegate &)>; using MidiMappingType = midi::MappingManager<10, 1>; +template + requires GettableParameter && + UpdatableParameter class ParameterUpdateNotifier { public: - ParameterUpdateNotifier(std::shared_ptr a_audio_params, + ParameterUpdateNotifier(std::shared_ptr a_audio_params, SendMessageCallback a_send_message) : audio_params{std::move(a_audio_params)}, send_message{a_send_message} @@ -61,14 +75,7 @@ class ParameterUpdateNotifier int update(const parameters::id_t ¶m, float value) { - auto message = AppMessage{ - parameters::Update{ - param, - value, - }, - std::nullopt, - }; - send_message(message); + send_message({parameters::Update{param, value}}); return audio_params->update(param, value); } @@ -78,82 +85,10 @@ class ParameterUpdateNotifier } private: - std::shared_ptr audio_params; + std::shared_ptr audio_params; SendMessageCallback send_message; }; -template -class ParameterObserver final : public parameters::ParameterObserver -{ -public: - explicit ParameterObserver( - std::shared_ptr a_persistence) - : is_save_throttled{true}, - persistence{std::move(a_persistence)}, - timer{"param save throttle", - pdMS_TO_TICKS(10'000), - false, - etl::delegate::create< - ParameterObserver, - &ParameterObserver::timer_callback>(*this)} - { - } - - void - notification(std::pair parameter) override - { - auto &[id, value] = parameter; - ESP_LOGI( - TAG, "notified about parameter change %s %f", id.data(), value); - if(!updated_parameters.available()) - { - ESP_LOGE(TAG, "no space available"); - return; - } - - updated_parameters[id] = value; - - if(!timer.is_active()) - { - if(pdPASS != timer.start(pdMS_TO_TICKS(5))) - { - ESP_LOGE(TAG, "Failed to start parameter observer timer"); - } - } - } - - void loop() - { - if(!is_save_throttled.test_and_set()) - { - persist_parameters(); - ESP_LOGI(TAG, "Parameters saved to NVS"); - } - } - -private: - void timer_callback() - { - is_save_throttled.clear(); - is_save_throttled.notify_all(); - } - - void persist_parameters() - { - for(const auto ¶m : updated_parameters) - { - persistence->save(param.first.data(), param.second); - } - - updated_parameters.clear(); - } - - std::atomic_flag is_save_throttled; - std::shared_ptr persistence; - os::Timer timer; - etl::map updated_parameters; -}; - template class PresetLoader { @@ -189,10 +124,7 @@ class PresetLoader presets::deserialise_live_parameters(*parameters, preset.parameters); - send_message({selected_preset::Notify{ - .selectedPresetId = id, - }, - std::nullopt}); + send_message({selected_preset::Notify{.selectedPresetId = id}}); return 0; } @@ -204,32 +136,34 @@ class PresetLoader SendMessageCallback send_message; }; -template +template + requires GettableParameter && + UpdatableParameter class MainThread { public: MainThread(SendMessageCallback a_send_message, + SendMessageCallback2 a_send_message2, Queue &a_in_queue, - midi::MidiUartBase *a_midi_uart, - std::shared_ptr a_audio_params, + std::shared_ptr a_audio_params, std::shared_ptr a_persistence, std::unique_ptr>> a_midi_mapping_storage, std::unique_ptr>> - a_presets_storage) + a_presets_storage, + etl::delegate()> a_get_midi_byte) : send_message{a_send_message}, + send_message2{a_send_message2}, in_queue{a_in_queue}, - parameter_observer{a_persistence}, clipping_throttle_timer{ - "clipping throttle", pdMS_TO_TICKS(1000), false}, + "clipping throttle", os::ms_to_ticks(1000), false}, midi_message_notify_timer{ "midi notify", - pdMS_TO_TICKS(100), + os::ms_to_ticks(100), true, os::Timer::Callback:: create( *this)}, - midi_uart{a_midi_uart}, last_midi_message{}, last_notified_midi_message{}, midi_decoder{std::make_unique( @@ -238,97 +172,35 @@ class MainThread *this))}, midi_mutex{}, audio_params{a_audio_params}, - cmd_handling{ - std::make_unique>( - a_audio_params, - parameters::CommandHandling:: - SendMessageCallback::create< - MainThread, - &MainThread::cmd_handling_send_message>(*this))}, presets_manager{std::make_shared( std::move(a_presets_storage))}, selected_preset_manager{ std::make_shared( - a_persistence)} + a_persistence)}, + get_midi_byte{a_get_midi_byte} { - auto create_and_load_parameter = [&](const parameters::id_t &name, - float minimum, - float maximum, - float default_value) - { - std::optional loaded_value; - float value; - int rc = a_persistence->load(name.data(), value); - if(rc != 0) - { - ESP_LOGW(TAG, "Parameter %s failed to load", name.data()); - goto out; - } - - loaded_value = value; - out: - auto range = maximum - minimum; - rc = a_audio_params->create_and_add_parameter( - name, - minimum, - maximum, - loaded_value.has_value() ? *loaded_value * range + minimum - : default_value); - if(rc != 0) - { - ESP_LOGE(TAG, "Failed to create parameter %s", name.c_str()); - } - }; - - // XXX: These are duplicated in the JUCE plugin, be sure to update both at - // the same time - create_and_load_parameter("ampGain", 0, 1, 0.5); - create_and_load_parameter("ampChannel", 0, 1, 0); - create_and_load_parameter("bass", 0, 1, 0.5); - create_and_load_parameter("middle", 0, 1, 0.5); - create_and_load_parameter("treble", 0, 1, 0.5); - //contour gets unstable when set to 0 - create_and_load_parameter("contour", 0.01, 1, 0.5); - create_and_load_parameter("volume", -30, 0, -15); - - create_and_load_parameter("noiseGateThreshold", -80, 0, -60); - create_and_load_parameter("noiseGateHysteresis", 0, 5, 0); - create_and_load_parameter("noiseGateAttack", 1, 50, 10); - create_and_load_parameter("noiseGateHold", 1, 250, 50); - create_and_load_parameter("noiseGateRelease", 1, 250, 50); - create_and_load_parameter("noiseGateBypass", 0, 1, 0); - - create_and_load_parameter("chorusRate", 0.1, 4, 0.95); - create_and_load_parameter("chorusDepth", 0, 1, 0.3); - create_and_load_parameter("chorusMix", 0, 1, 0.8); - create_and_load_parameter("chorusBypass", 0, 1, 1); - - create_and_load_parameter("wahPosition", 0, 1, 0.5); - create_and_load_parameter("wahVocal", 0, 1, 0); - create_and_load_parameter("wahBypass", 0, 1, 1); - - a_audio_params->add_observer(parameter_observer); - - parameter_notifier = std::make_shared( - a_audio_params, a_send_message); + parameter_notifier = + std::make_shared>( + a_audio_params, a_send_message); midi_mapping_manager = std::make_shared( std::move(a_midi_mapping_storage)); - preset_loader = std::make_shared>( + preset_loader = std::make_shared< + PresetLoader>>( parameter_notifier, presets_manager, selected_preset_manager, send_message); - midi_message_handler = std::make_shared< - MidiMessageHandler>>( + midi_message_handler = std::make_shared, + MidiMappingType, + PresetLoader>>>( parameter_notifier, midi_mapping_manager, preset_loader); - BaseType_t rc = midi_message_notify_timer.start(portMAX_DELAY); - if(rc != pdPASS) + auto rc = midi_message_notify_timer.start(os::max_delay()); + if(rc != os::timer_error::TIMER_START_SUCCESS) { ESP_LOGE(TAG, "Failed to start midi message timer"); } @@ -340,21 +212,20 @@ class MainThread void loop() { { - auto byte = midi_uart->get_byte(0); + auto byte = get_midi_byte(); while(byte.has_value()) { ESP_LOGI(TAG, "midi got byte 0x%02x", *byte); midi_decoder->decode(*byte); - byte = midi_uart->get_byte(0); + byte = get_midi_byte(); } } - if(AppMessage message; in_queue.receive(&message, 0)) + if(AppMessage message; + queue_error::SUCCESS == in_queue.receive(&message, 0)) { - auto fd = message.second; - - std::visit([this, fd](const auto &m) { handle_message(m, fd); }, - message.first); + std::visit([this](const auto &m) { this->handle_message(m); }, + message); } if(!clipping_throttle_timer.is_active()) @@ -362,10 +233,9 @@ class MainThread if(!events::input_clipped.test_and_set()) { ESP_LOGI(TAG, "input was clipped"); - send_message({events::InputClipped{}, std::nullopt}); - BaseType_t rc = - clipping_throttle_timer.start(pdMS_TO_TICKS(10)); - if(rc != pdPASS) + send_message({events::InputClipped{}}); + auto rc = clipping_throttle_timer.start(os::ms_to_ticks(10)); + if(rc != os::timer_error::TIMER_START_SUCCESS) { ESP_LOGE(TAG, "Failed to start clipping throttle timer"); } @@ -374,48 +244,42 @@ class MainThread if(!events::output_clipped.test_and_set()) { ESP_LOGI(TAG, "output was clipped"); - send_message({events::OutputClipped{}, std::nullopt}); - BaseType_t rc = - clipping_throttle_timer.start(pdMS_TO_TICKS(10)); - if(rc != pdPASS) + send_message({events::OutputClipped{}}); + auto rc = clipping_throttle_timer.start(os::ms_to_ticks(10)); + if(rc != os::timer_error::TIMER_START_SUCCESS) { ESP_LOGE(TAG, "Failed to start clipping throttle timer"); } } } - parameter_observer.loop(); - if(!is_midi_notify_waiting.test_and_set() && last_midi_message.has_value() && last_notified_midi_message != *last_midi_message) { last_notified_midi_message = *last_midi_message; - send_message( - {midi::MessageReceived{*last_midi_message}, std::nullopt}); + send_message({midi::MessageReceived{*last_midi_message}}); } } private: void - handle_message(const selected_preset::SelectedPresetApiMessage &app_message, - std::optional) + handle_message(const selected_preset::SelectedPresetApiMessage &app_message) { auto response = std::visit( [this](const auto &message) -> std::optional - { return handle_selected_preset_message(message); }, + { return this->handle_selected_preset_message(message); }, app_message); if(response.has_value()) { - send_message({*response, std::nullopt}); + send_message({*response}); } } - void handle_message(const presets::PresetsApiMessage &app_message, - std::optional) + void handle_message(const presets::PresetsApiMessage &app_message) { auto response = std::visit( [&](const auto &presets_message) @@ -432,10 +296,9 @@ class MainThread const presets::PresetData &preset) { send_message({presets::Notify{ - .id = id, - .preset = preset, - }, - std::nullopt}); + .id = id, + .preset = preset, + }}); }); } else if constexpr(std::is_same_v fd) + void handle_message(const parameters::ApiMessage &app_message) { - if(!fd.has_value()) - { - ESP_LOGE(TAG, "Must always have fd"); - } + std::visit( + overloaded{ + [&](const parameters::Initialise &) + { + ESP_LOGD(TAG, "initialise"); - cmd_handling->dispatch(app_message, *fd); - } + for(const auto &[key, value] : *audio_params) + { + parameters::Update message = { + .id{parameters::id_t{key}}, + .value{value->get()}, + }; - void handle_message(const midi::MappingApiMessage &app_message, - std::optional) + send_message(message); + } + }, + [](const parameters::Update &message) { assert(false); }, + }, + app_message); + } // namespace shrapnel + + void handle_message(const midi::MappingApiMessage &app_message) { std::scoped_lock lock{midi_mutex}; @@ -524,15 +398,7 @@ class MainThread auto mappings = midi_mapping_manager->get(); for(const auto &[id, mapping] : *mappings) { - send_message({ - midi::Update{ - { - id, - mapping, - }, - }, - std::nullopt, - }); + return {midi::Update{{id, mapping}}}; } } else if constexpr(std::is_same_v) + void handle_message(const midi::Message &message) { on_midi_message(message); } - void handle_message(const events::ApiMessage &app_message, - std::optional) + void handle_message(const events::ApiMessage &app_message) { // nothing to do } + void handle_message(const ParameterUpdateMessage &app_message) + { + ESP_LOGI(TAG, "TODO handle parameter update"); + + etl::string<100> buffer; + etl::string_stream stream{buffer}; + stream << app_message; + ESP_LOGI(TAG, "%s", buffer.data()); + } + std::optional handle_selected_preset_message(selected_preset::Read) { @@ -640,37 +515,31 @@ class MainThread void clear_midi_notify_waiting() { is_midi_notify_waiting.clear(); }; - void cmd_handling_send_message(const parameters::ApiMessage &m, - std::optional fd) - { - send_message({m, fd}); - } - SendMessageCallback send_message; + SendMessageCallback2 send_message2; Queue &in_queue; - ParameterObserver parameter_observer; os::Timer clipping_throttle_timer; os::Timer midi_message_notify_timer; std::atomic_flag is_midi_notify_waiting; - midi::MidiUartBase *midi_uart; std::optional last_midi_message; std::optional last_notified_midi_message; std::unique_ptr midi_decoder; std::mutex midi_mutex; std::shared_ptr midi_mapping_manager; - std::shared_ptr>> + std::shared_ptr, + MidiMappingType, + PresetLoader>>> midi_message_handler; - std::shared_ptr audio_params; - std::unique_ptr>> - cmd_handling; + std::shared_ptr audio_params; std::shared_ptr presets_manager; std::shared_ptr selected_preset_manager; - std::shared_ptr parameter_notifier; - std::shared_ptr> preset_loader; + std::shared_ptr> + parameter_notifier; + std::shared_ptr>> + preset_loader; + etl::delegate()> get_midi_byte; }; } // namespace shrapnel \ No newline at end of file diff --git a/firmware/components/messages/CMakeLists.txt b/shrapnel/messages/CMakeLists.txt similarity index 67% rename from firmware/components/messages/CMakeLists.txt rename to shrapnel/messages/CMakeLists.txt index 404df7af..3a7bddb0 100644 --- a/firmware/components/messages/CMakeLists.txt +++ b/shrapnel/messages/CMakeLists.txt @@ -1,15 +1,15 @@ -include(../../../thirdparty/esp-idf-components/nanopb/nanopb-generate-cpp.cmake) +include(../../thirdparty/esp-idf-components/nanopb/nanopb-generate-cpp.cmake) add_library(messages STATIC) add_library(shrapnel::messages ALIAS messages) set(NANOPB_GENERATE_CPP_APPEND_PATH ON) nanopb_generate_cpp(PROTO_SRCS PROTO_HDRS - ../../../proto/audio_events.proto - ../../../proto/cmd_handling.proto - ../../../proto/shrapnel.proto - ../../../proto/presets.proto - ../../../proto/midi_mapping.proto) + ../../proto/audio_events.proto + ../../proto/cmd_handling.proto + ../../proto/shrapnel.proto + ../../proto/presets.proto + ../../proto/midi_mapping.proto) target_include_directories(messages PUBLIC include diff --git a/shrapnel/messages/include/messages.h b/shrapnel/messages/include/messages.h new file mode 100644 index 00000000..607a967b --- /dev/null +++ b/shrapnel/messages/include/messages.h @@ -0,0 +1,103 @@ +/* + * Copyright 2022 Barabas Raffai + * + * This file is part of ShrapnelDSP. + * + * ShrapnelDSP is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * ShrapnelDSP is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * ShrapnelDSP. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +template +struct overloaded : Ts... +{ + using Ts::operator()...; +}; + +template +overloaded(Ts...) -> overloaded; + +/** Parameter updated by an API client */ +struct ParameterUpdateApi final +{ + shrapnel::parameters::Update update; + /// File descriptor of connection to the API client + std::optional fd; + + std::strong_ordering + operator<=>(const ParameterUpdateApi &other) const = default; +}; + +/** Parameter updated by a the plugin host */ +struct ParameterUpdateHost final +{ + shrapnel::parameters::Update update; + + std::strong_ordering + operator<=>(const ParameterUpdateHost &other) const = default; +}; + +/** Parameter updated by another source */ +struct ParameterUpdateOther final +{ + shrapnel::parameters::Update update; + + std::strong_ordering + operator<=>(const ParameterUpdateOther &other) const = default; +}; + +using ParameterUpdateMessage = + std::variant; + +etl::string_stream &operator<<(etl::string_stream &out, + const ParameterUpdateMessage &self); + +using ApiMessage = + std::variant; + +using AppMessage = + std::variant; + +etl::string_stream &operator<<(etl::string_stream &out, const ApiMessage &self); + +namespace shrapnel::api { + +template <> +std::optional> to_bytes(const ApiMessage &message, + std::span buffer); + +template <> +std::optional from_bytes(std::span buffer); + +} // namespace shrapnel::api \ No newline at end of file diff --git a/firmware/components/messages/src/messages.cpp b/shrapnel/messages/src/messages.cpp similarity index 67% rename from firmware/components/messages/src/messages.cpp rename to shrapnel/messages/src/messages.cpp index 4a4674f0..9083395a 100644 --- a/firmware/components/messages/src/messages.cpp +++ b/shrapnel/messages/src/messages.cpp @@ -17,11 +17,9 @@ * ShrapnelDSP. If not, see . */ -#include -#include - #include "messages.h" #include "shrapnel.pb.h" +#include #include #include @@ -181,3 +179,84 @@ std::optional from_bytes(std::span buffer) } } // namespace shrapnel::api + +etl::string_stream &operator<<(etl::string_stream &out, const ApiMessage &self) +{ + std::visit( + [&](const auto &message) + { + using T = std::decay_t; + + if constexpr(std::is_same_v) + { + out << "" << message; + } + else if constexpr(std::is_same_v) + { + out << "" << message; + } + else if constexpr(std::is_same_v) + { + out << "" << message; + } + else if constexpr(std::is_same_v) + { + out << "" << message; + } + else if constexpr(std::is_same_v< + T, + shrapnel::presets::PresetsApiMessage>) + { + out << "" << message; + } + else if constexpr(std::is_same_v) + { + out << "" << message; + } + else + { + out << ""; + } + }, + self); + + return out; +} + +etl::string_stream &operator<<(etl::string_stream &out, + const ParameterUpdateApi &self) +{ + return out << "{ update=" << self.update << " fd=" << self.fd << " }"; +} + +etl::string_stream &operator<<(etl::string_stream &out, + const ParameterUpdateHost &self) +{ + return out << "{ update=" << self.update << " }"; +} + +etl::string_stream &operator<<(etl::string_stream &out, + const ParameterUpdateOther &self) +{ + return out << "{ update=" << self.update << " }"; +} + +etl::string_stream &operator<<(etl::string_stream &out, + const ParameterUpdateMessage &self) +{ + std::visit( + overloaded{ + [&](const ParameterUpdateApi &message) + { out << "" << message; }, + [&](const ParameterUpdateHost &message) + { out << "" << message; }, + [&](const ParameterUpdateOther &message) + { out << "" << message; }, + }, + self); + + return out; +} diff --git a/firmware/components/midi_handling/CMakeLists.txt b/shrapnel/midi_handling/CMakeLists.txt similarity index 100% rename from firmware/components/midi_handling/CMakeLists.txt rename to shrapnel/midi_handling/CMakeLists.txt diff --git a/firmware/components/midi_handling/include/midi_handling.h b/shrapnel/midi_handling/include/midi_handling.h similarity index 100% rename from firmware/components/midi_handling/include/midi_handling.h rename to shrapnel/midi_handling/include/midi_handling.h diff --git a/firmware/components/midi_handling/test/test_midi_handling.cpp b/shrapnel/midi_handling/test/test_midi_handling.cpp similarity index 100% rename from firmware/components/midi_handling/test/test_midi_handling.cpp rename to shrapnel/midi_handling/test/test_midi_handling.cpp diff --git a/firmware/components/midi_mapping/CMakeLists.txt b/shrapnel/midi_mapping/CMakeLists.txt similarity index 91% rename from firmware/components/midi_mapping/CMakeLists.txt rename to shrapnel/midi_mapping/CMakeLists.txt index 60d53980..c8e00458 100644 --- a/firmware/components/midi_mapping/CMakeLists.txt +++ b/shrapnel/midi_mapping/CMakeLists.txt @@ -1,11 +1,11 @@ -include(../../../thirdparty/esp-idf-components/nanopb/nanopb-generate-cpp.cmake) +include(../../thirdparty/esp-idf-components/nanopb/nanopb-generate-cpp.cmake) add_library(midi_mapping STATIC) add_library(shrapnel::midi_mapping ALIAS midi_mapping) set(NANOPB_GENERATE_CPP_APPEND_PATH ON) nanopb_generate_cpp(PROTO_SRCS PROTO_HDRS - ../../../proto/midi_mapping.proto) + ../../proto/midi_mapping.proto) target_sources(midi_mapping PRIVATE diff --git a/firmware/components/midi_mapping/include/midi_mapping.h b/shrapnel/midi_mapping/include/midi_mapping.h similarity index 100% rename from firmware/components/midi_mapping/include/midi_mapping.h rename to shrapnel/midi_mapping/include/midi_mapping.h diff --git a/firmware/components/midi_mapping/include/midi_mapping_api.h b/shrapnel/midi_mapping/include/midi_mapping_api.h similarity index 100% rename from firmware/components/midi_mapping/include/midi_mapping_api.h rename to shrapnel/midi_mapping/include/midi_mapping_api.h diff --git a/firmware/components/midi_mapping/src/midi_mapping.cpp b/shrapnel/midi_mapping/src/midi_mapping.cpp similarity index 100% rename from firmware/components/midi_mapping/src/midi_mapping.cpp rename to shrapnel/midi_mapping/src/midi_mapping.cpp diff --git a/firmware/components/midi_mapping/src/midi_mapping_api.cpp b/shrapnel/midi_mapping/src/midi_mapping_api.cpp similarity index 100% rename from firmware/components/midi_mapping/src/midi_mapping_api.cpp rename to shrapnel/midi_mapping/src/midi_mapping_api.cpp diff --git a/firmware/components/midi_mapping/test/test_midi_mapping.cpp b/shrapnel/midi_mapping/test/test_midi_mapping.cpp similarity index 100% rename from firmware/components/midi_mapping/test/test_midi_mapping.cpp rename to shrapnel/midi_mapping/test/test_midi_mapping.cpp diff --git a/firmware/components/midi_protocol/CMakeLists.txt b/shrapnel/midi_protocol/CMakeLists.txt similarity index 96% rename from firmware/components/midi_protocol/CMakeLists.txt rename to shrapnel/midi_protocol/CMakeLists.txt index 82485e0f..92fe2595 100644 --- a/firmware/components/midi_protocol/CMakeLists.txt +++ b/shrapnel/midi_protocol/CMakeLists.txt @@ -6,7 +6,6 @@ target_link_libraries(midi_protocol PUBLIC - idf::driver idf::log shrapnel::etl PRIVATE diff --git a/firmware/components/midi_protocol/include/midi_protocol.h b/shrapnel/midi_protocol/include/midi_protocol.h similarity index 100% rename from firmware/components/midi_protocol/include/midi_protocol.h rename to shrapnel/midi_protocol/include/midi_protocol.h diff --git a/shrapnel/midi_protocol/include/midi_uart.h b/shrapnel/midi_protocol/include/midi_uart.h new file mode 100644 index 00000000..eea00e8d --- /dev/null +++ b/shrapnel/midi_protocol/include/midi_uart.h @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Barabas Raffai + * + * This file is part of ShrapnelDSP. + * + * ShrapnelDSP is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * ShrapnelDSP is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * ShrapnelDSP. If not, see . + */ + +#pragma once + +#include +#include +#include + +namespace shrapnel::midi { + +template +class MidiUartBase +{ +public: + virtual std::optional get_byte(T timeout) = 0; +}; + +} // namespace shrapnel::midi + diff --git a/firmware/components/midi_protocol/src/midi.cpp b/shrapnel/midi_protocol/src/midi.cpp similarity index 100% rename from firmware/components/midi_protocol/src/midi.cpp rename to shrapnel/midi_protocol/src/midi.cpp diff --git a/shrapnel/midi_protocol/src/midi_uart.cpp b/shrapnel/midi_protocol/src/midi_uart.cpp new file mode 100644 index 00000000..e69de29b diff --git a/firmware/components/midi_protocol/test/midi_util.cpp b/shrapnel/midi_protocol/test/midi_util.cpp similarity index 100% rename from firmware/components/midi_protocol/test/midi_util.cpp rename to shrapnel/midi_protocol/test/midi_util.cpp diff --git a/firmware/components/midi_protocol/test/midi_util.h b/shrapnel/midi_protocol/test/midi_util.h similarity index 100% rename from firmware/components/midi_protocol/test/midi_util.h rename to shrapnel/midi_protocol/test/midi_util.h diff --git a/firmware/components/midi_protocol/test/test_midi_decoder.cpp b/shrapnel/midi_protocol/test/test_midi_decoder.cpp similarity index 100% rename from firmware/components/midi_protocol/test/test_midi_decoder.cpp rename to shrapnel/midi_protocol/test/test_midi_decoder.cpp diff --git a/firmware/components/midi_protocol/test/test_midi_message.cpp b/shrapnel/midi_protocol/test/test_midi_message.cpp similarity index 100% rename from firmware/components/midi_protocol/test/test_midi_message.cpp rename to shrapnel/midi_protocol/test/test_midi_message.cpp diff --git a/firmware/components/os/CMakeLists.txt b/shrapnel/os/CMakeLists.txt similarity index 75% rename from firmware/components/os/CMakeLists.txt rename to shrapnel/os/CMakeLists.txt index a7d08b4c..b26b28d1 100644 --- a/firmware/components/os/CMakeLists.txt +++ b/shrapnel/os/CMakeLists.txt @@ -6,12 +6,12 @@ target_include_directories(os PUBLIC include) target_link_libraries(os PUBLIC - shrapnel::etl - idf::freertos) + shrapnel::etl) if(ESP_PLATFORM) target_sources(os PRIVATE esp32/timer_impl.cpp universal/timer.cpp esp32/debug.cpp) - target_include_directories(os PRIVATE esp32) + target_include_directories(os PUBLIC esp32) + target_link_libraries(os PRIVATE idf::freertos) return() endif() diff --git a/firmware/components/cmd_handling/src/cmd_handling.cpp b/shrapnel/os/desktop/os/os.h similarity index 76% rename from firmware/components/cmd_handling/src/cmd_handling.cpp rename to shrapnel/os/desktop/os/os.h index b14eb958..3b3cadd0 100644 --- a/firmware/components/cmd_handling/src/cmd_handling.cpp +++ b/shrapnel/os/desktop/os/os.h @@ -17,4 +17,13 @@ * ShrapnelDSP. If not, see . */ -#include "cmd_handling.h" +#pragma once + +#include + +namespace shrapnel::os { + +constexpr uint32_t ms_to_ticks(uint32_t milliseconds) { return milliseconds; } +constexpr uint32_t max_delay() { return UINT32_MAX; } + +} // namespace shrapnel::os diff --git a/firmware/components/os/desktop/timer_impl.cpp b/shrapnel/os/desktop/timer_impl.cpp similarity index 72% rename from firmware/components/os/desktop/timer_impl.cpp rename to shrapnel/os/desktop/timer_impl.cpp index b70e187c..c642ae9d 100644 --- a/firmware/components/os/desktop/timer_impl.cpp +++ b/shrapnel/os/desktop/timer_impl.cpp @@ -23,8 +23,8 @@ namespace shrapnel::os { Timer::impl::impl(const char *pcTimerName, - TickType_t xTimerPeriod, - const UBaseType_t uxAutoReload, + uint32_t xTimerPeriod, + bool uxAutoReload, std::optional> callback) : callback(callback) { @@ -35,18 +35,20 @@ Timer::impl::impl(const char *pcTimerName, uxAutoReload); } -BaseType_t Timer::impl::is_active() const { return is_running; } +bool Timer::impl::is_active() const { return is_running; } -BaseType_t Timer::impl::start(TickType_t xBlockTime) +timer_error Timer::impl::start(uint32_t xBlockTime) { is_running = true; - return timers.start(id); + return timers.start(id) ? timer_error::TIMER_START_SUCCESS + : timer_error::TIMER_START_FAILURE; } -BaseType_t Timer::impl::stop(TickType_t xBlockTime) +timer_error Timer::impl::stop(uint32_t xBlockTime) { is_running = false; - return timers.stop(id); + return timers.stop(id) ? timer_error::TIMER_START_SUCCESS + : timer_error::TIMER_START_FAILURE; } void Timer::impl::tick(uint32_t tick_count) { timers.tick(tick_count); } diff --git a/firmware/components/os/desktop/timer_impl.h b/shrapnel/os/desktop/timer_impl.h similarity index 89% rename from firmware/components/os/desktop/timer_impl.h rename to shrapnel/os/desktop/timer_impl.h index 70166fa1..86b4c22d 100644 --- a/firmware/components/os/desktop/timer_impl.h +++ b/shrapnel/os/desktop/timer_impl.h @@ -25,13 +25,13 @@ namespace shrapnel::os { struct Timer::impl { impl(const char *pcTimerName, - TickType_t xTimerPeriod, - const UBaseType_t uxAutoReload, + uint32_t xTimerPeriod, + bool uxAutoReload, std::optional callback); - BaseType_t is_active() const; - BaseType_t start(TickType_t xBlockTime); - BaseType_t stop(TickType_t xBlockTime); + [[nodiscard]] bool is_active() const; + timer_error start(uint32_t xBlockTime); + timer_error stop(uint32_t xBlockTime); // Advance tick count, firing callbacks for any expired timers diff --git a/firmware/components/os/esp32/debug.cpp b/shrapnel/os/esp32/debug.cpp similarity index 100% rename from firmware/components/os/esp32/debug.cpp rename to shrapnel/os/esp32/debug.cpp diff --git a/shrapnel/os/esp32/os/os.h b/shrapnel/os/esp32/os/os.h new file mode 100644 index 00000000..b82a31f5 --- /dev/null +++ b/shrapnel/os/esp32/os/os.h @@ -0,0 +1,32 @@ +/* + * Copyright 2022 Barabas Raffai + * + * This file is part of ShrapnelDSP. + * + * ShrapnelDSP is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * ShrapnelDSP is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * ShrapnelDSP. If not, see . + */ + +#pragma once + +#include + +namespace shrapnel::os { + +constexpr uint32_t ms_to_ticks(uint32_t milliseconds) +{ + return pdMS_TO_TICKS(milliseconds); +} +constexpr uint32_t max_delay() { return portMAX_DELAY; } + +} // namespace shrapnel::os diff --git a/firmware/components/os/esp32/timer_impl.cpp b/shrapnel/os/esp32/timer_impl.cpp similarity index 75% rename from firmware/components/os/esp32/timer_impl.cpp rename to shrapnel/os/esp32/timer_impl.cpp index 94f22e59..b7068f2d 100644 --- a/firmware/components/os/esp32/timer_impl.cpp +++ b/shrapnel/os/esp32/timer_impl.cpp @@ -37,8 +37,8 @@ extern "C" void timer_callback(TimerHandle_t a_timer) extern "C" void null_callback(TimerHandle_t) {} Timer::impl::impl(const char *pcTimerName, - TickType_t xTimerPeriod, - const UBaseType_t uxAutoReload, + uint32_t xTimerPeriod, + bool uxAutoReload, std::optional> callback) : callback{callback} { @@ -55,16 +55,26 @@ Timer::impl::~impl() assert(rc == pdPASS); } -BaseType_t Timer::impl::is_active() const { return xTimerIsTimerActive(timer); } +bool Timer::impl::is_active() const { return xTimerIsTimerActive(timer); } -BaseType_t Timer::impl::start(TickType_t xBlockTime) +timer_error Timer::impl::start(TickType_t xBlockTime) { - return xTimerStart(timer, xBlockTime); + if(pdPASS != xTimerStart(timer, xBlockTime)) + { + return timer_error::TIMER_START_FAILURE; + } + + return timer_error::TIMER_START_SUCCESS; } -BaseType_t Timer::impl::stop(TickType_t xBlockTime) +timer_error Timer::impl::stop(TickType_t xBlockTime) { - return xTimerStop(timer, xBlockTime); + if(pdPASS != xTimerStop(timer, xBlockTime)) + { + return timer_error::TIMER_START_FAILURE; + } + + return timer_error::TIMER_START_SUCCESS; } } // namespace shrapnel::os diff --git a/firmware/components/os/esp32/timer_impl.h b/shrapnel/os/esp32/timer_impl.h similarity index 85% rename from firmware/components/os/esp32/timer_impl.h rename to shrapnel/os/esp32/timer_impl.h index d772e1ad..40b67c4b 100644 --- a/firmware/components/os/esp32/timer_impl.h +++ b/shrapnel/os/esp32/timer_impl.h @@ -28,15 +28,15 @@ extern "C" void timer_callback(TimerHandle_t a_timer); struct Timer::impl { impl(const char *pcTimerName, - TickType_t xTimerPeriod, - UBaseType_t uxAutoReload, + uint32_t xTimerPeriod, + bool uxAutoReload, std::optional> callback); ~impl(); - [[nodiscard]] BaseType_t is_active() const; - BaseType_t start(TickType_t xBlockTime); - BaseType_t stop(TickType_t xBlockTime); + [[nodiscard]] bool is_active() const; + timer_error start(TickType_t xBlockTime); + timer_error stop(TickType_t xBlockTime); friend void shrapnel::os::timer_callback(TimerHandle_t a_timer); TimerHandle_t timer; diff --git a/firmware/components/os/include/os/debug.h b/shrapnel/os/include/os/debug.h similarity index 100% rename from firmware/components/os/include/os/debug.h rename to shrapnel/os/include/os/debug.h diff --git a/firmware/components/os/include/os/queue.h b/shrapnel/os/include/os/queue.h similarity index 72% rename from firmware/components/os/include/os/queue.h rename to shrapnel/os/include/os/queue.h index 99d133f1..bd43061f 100644 --- a/firmware/components/os/include/os/queue.h +++ b/shrapnel/os/include/os/queue.h @@ -19,12 +19,8 @@ #pragma once -#include "FreeRTOSConfig.h" #include "etl/deque.h" -#include "freertos/FreeRTOS.h" -#include "freertos/queue.h" #include -#include #include #include #include @@ -33,38 +29,41 @@ namespace shrapnel { +enum class queue_error +{ + SUCCESS = 0, + ERROR = 1, + EMPTY = 2, + FULL = 3, +}; + template class QueueBase { public: using value_type = T; - QueueBase(int number_of_elements) { (void)number_of_elements; }; - - virtual BaseType_t receive(T *out, TickType_t time_to_wait) = 0; - virtual BaseType_t send(const T *in, TickType_t time_to_wait) = 0; + [[nodiscard]] virtual queue_error receive(T *out, + uint32_t time_to_wait) = 0; + [[nodiscard]] virtual queue_error send(const T *in, + uint32_t time_to_wait) = 0; }; template requires(MAX_SIZE > 0) && (MAX_SIZE < PTRDIFF_MAX) class Queue final : public QueueBase { - using ticks = std::chrono::duration; + using ticks = std::chrono::duration; public: - Queue() - : QueueBase(MAX_SIZE), - used_semaphore{0}, - free_semaphore{MAX_SIZE} - { - } + Queue() : QueueBase(), used_semaphore{0}, free_semaphore{MAX_SIZE} {} - [[nodiscard]] BaseType_t receive(T *out, TickType_t time_to_wait) override + [[nodiscard]] queue_error receive(T *out, uint32_t time_to_wait) override { // block until an item is available bool success = used_semaphore.try_acquire_for(ticks(time_to_wait)); if(!success) - return errQUEUE_EMPTY; + return queue_error::EMPTY; // Receive the item { @@ -73,15 +72,15 @@ class Queue final : public QueueBase queue.pop(); } free_semaphore.release(); - return pdPASS; + return queue_error::SUCCESS; } - [[nodiscard]] BaseType_t send(const T *in, TickType_t time_to_wait) override + [[nodiscard]] queue_error send(const T *in, uint32_t time_to_wait) override { // block until a space is available bool success = free_semaphore.try_acquire_for(ticks(time_to_wait)); if(!success) - return errQUEUE_FULL; + return queue_error::FULL; // Add item to queue, and notify receivers { @@ -89,7 +88,7 @@ class Queue final : public QueueBase queue.push(*in); } used_semaphore.release(); - return pdPASS; + return queue_error::SUCCESS; } private: @@ -97,7 +96,6 @@ class Queue final : public QueueBase used_semaphore; std::counting_semaphore(MAX_SIZE)> free_semaphore; - etl::deque queue_storage; std::queue> queue; std::mutex mutex; }; diff --git a/firmware/components/os/include/os/timer.h b/shrapnel/os/include/os/timer.h similarity index 80% rename from firmware/components/os/include/os/timer.h rename to shrapnel/os/include/os/timer.h index c6763728..e580e2f2 100644 --- a/firmware/components/os/include/os/timer.h +++ b/shrapnel/os/include/os/timer.h @@ -19,28 +19,33 @@ #pragma once #include "etl/delegate.h" -#include "freertos/FreeRTOS.h" #include #include namespace shrapnel::os { +enum class timer_error +{ + TIMER_START_SUCCESS, + TIMER_START_FAILURE, +}; + class Timer final { public: using Callback = etl::delegate; Timer(const char *pcTimerName, - TickType_t xTimerPeriod, - UBaseType_t uxAutoReload, + uint32_t xTimerPeriod, + bool uxAutoReload, std::optional callback = std::nullopt); ~Timer(); - [[nodiscard]] BaseType_t is_active() const; + [[nodiscard]] bool is_active() const; - [[nodiscard]] BaseType_t start(TickType_t xBlockTime); - [[nodiscard]] BaseType_t stop(TickType_t xBlockTime); + [[nodiscard]] timer_error start(uint32_t xBlockTime); + [[nodiscard]] timer_error stop(uint32_t xBlockTime); struct impl; diff --git a/firmware/components/os/universal/timer.cpp b/shrapnel/os/universal/timer.cpp similarity index 82% rename from firmware/components/os/universal/timer.cpp rename to shrapnel/os/universal/timer.cpp index 5a07675e..ec6a4c15 100644 --- a/firmware/components/os/universal/timer.cpp +++ b/shrapnel/os/universal/timer.cpp @@ -23,8 +23,8 @@ namespace shrapnel::os { Timer::Timer(const char *pcTimerName, - TickType_t xTimerPeriod, - const UBaseType_t uxAutoReload, + uint32_t xTimerPeriod, + bool uxAutoReload, std::optional> callback) { p_impl = std::make_unique( @@ -33,14 +33,14 @@ Timer::Timer(const char *pcTimerName, Timer::~Timer() {} -BaseType_t Timer::is_active() const { return p_impl->is_active(); } +bool Timer::is_active() const { return p_impl->is_active(); } -BaseType_t Timer::start(TickType_t xBlockTime) +timer_error Timer::start(uint32_t xBlockTime) { return p_impl->start(xBlockTime); } -BaseType_t Timer::stop(TickType_t xBlockTime) +timer_error Timer::stop(uint32_t xBlockTime) { return p_impl->stop(xBlockTime); } diff --git a/firmware/components/persistence/CMakeLists.txt b/shrapnel/persistence/CMakeLists.txt similarity index 100% rename from firmware/components/persistence/CMakeLists.txt rename to shrapnel/persistence/CMakeLists.txt diff --git a/firmware/components/persistence/include/crud.h b/shrapnel/persistence/include/crud.h similarity index 100% rename from firmware/components/persistence/include/crud.h rename to shrapnel/persistence/include/crud.h diff --git a/firmware/components/persistence/include/persistence.h b/shrapnel/persistence/include/persistence.h similarity index 100% rename from firmware/components/persistence/include/persistence.h rename to shrapnel/persistence/include/persistence.h diff --git a/firmware/components/persistence/test/include/mock_persistence.h b/shrapnel/persistence/test/include/mock_persistence.h similarity index 100% rename from firmware/components/persistence/test/include/mock_persistence.h rename to shrapnel/persistence/test/include/mock_persistence.h diff --git a/firmware/components/presets/CMakeLists.txt b/shrapnel/presets/CMakeLists.txt similarity index 75% rename from firmware/components/presets/CMakeLists.txt rename to shrapnel/presets/CMakeLists.txt index a0035cce..df027b0a 100644 --- a/firmware/components/presets/CMakeLists.txt +++ b/shrapnel/presets/CMakeLists.txt @@ -1,12 +1,12 @@ -include(../../../thirdparty/esp-idf-components/nanopb/nanopb-generate-cpp.cmake) +include(../../thirdparty/esp-idf-components/nanopb/nanopb-generate-cpp.cmake) add_library(presets STATIC) add_library(shrapnel::presets ALIAS presets) set(NANOPB_GENERATE_CPP_APPEND_PATH ON) nanopb_generate_cpp(PROTO_SRCS PROTO_HDRS - ../../../proto/presets.proto - ../../../proto/selected_preset.proto) + ../../proto/presets.proto + ../../proto/selected_preset.proto) target_sources(presets PRIVATE @@ -29,6 +29,4 @@ target_link_libraries(presets shrapnel::api shrapnel::etl shrapnel::persistence - shrapnel::nanopb - PRIVATE - cppcodec) + shrapnel::nanopb) diff --git a/firmware/components/presets/include/preset_serialisation.h b/shrapnel/presets/include/preset_serialisation.h similarity index 100% rename from firmware/components/presets/include/preset_serialisation.h rename to shrapnel/presets/include/preset_serialisation.h diff --git a/firmware/components/presets/include/presets.h b/shrapnel/presets/include/presets.h similarity index 100% rename from firmware/components/presets/include/presets.h rename to shrapnel/presets/include/presets.h diff --git a/firmware/components/presets/include/presets_api.h b/shrapnel/presets/include/presets_api.h similarity index 100% rename from firmware/components/presets/include/presets_api.h rename to shrapnel/presets/include/presets_api.h diff --git a/firmware/components/presets/include/presets_manager.h b/shrapnel/presets/include/presets_manager.h similarity index 100% rename from firmware/components/presets/include/presets_manager.h rename to shrapnel/presets/include/presets_manager.h diff --git a/firmware/components/presets/include/selected_preset_api.h b/shrapnel/presets/include/selected_preset_api.h similarity index 100% rename from firmware/components/presets/include/selected_preset_api.h rename to shrapnel/presets/include/selected_preset_api.h diff --git a/firmware/components/presets/include/selected_preset_manager.h b/shrapnel/presets/include/selected_preset_manager.h similarity index 100% rename from firmware/components/presets/include/selected_preset_manager.h rename to shrapnel/presets/include/selected_preset_manager.h diff --git a/firmware/components/presets/src/preset_manager.cpp b/shrapnel/presets/src/preset_manager.cpp similarity index 100% rename from firmware/components/presets/src/preset_manager.cpp rename to shrapnel/presets/src/preset_manager.cpp diff --git a/firmware/components/presets/src/presets_api.cpp b/shrapnel/presets/src/presets_api.cpp similarity index 100% rename from firmware/components/presets/src/presets_api.cpp rename to shrapnel/presets/src/presets_api.cpp diff --git a/firmware/components/presets/src/presets_to_string.cpp b/shrapnel/presets/src/presets_to_string.cpp similarity index 100% rename from firmware/components/presets/src/presets_to_string.cpp rename to shrapnel/presets/src/presets_to_string.cpp diff --git a/firmware/components/presets/src/selected_preset_api.cpp b/shrapnel/presets/src/selected_preset_api.cpp similarity index 100% rename from firmware/components/presets/src/selected_preset_api.cpp rename to shrapnel/presets/src/selected_preset_api.cpp diff --git a/firmware/components/presets/src/selected_preset_manager.cpp b/shrapnel/presets/src/selected_preset_manager.cpp similarity index 100% rename from firmware/components/presets/src/selected_preset_manager.cpp rename to shrapnel/presets/src/selected_preset_manager.cpp diff --git a/firmware/components/presets/src/selected_preset_to_string.cpp b/shrapnel/presets/src/selected_preset_to_string.cpp similarity index 100% rename from firmware/components/presets/src/selected_preset_to_string.cpp rename to shrapnel/presets/src/selected_preset_to_string.cpp diff --git a/test/.idea/.gitignore b/test/.idea/.gitignore deleted file mode 100644 index 13566b81..00000000 --- a/test/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/test/.idea/.name b/test/.idea/.name deleted file mode 100644 index e2399237..00000000 --- a/test/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -ShrapnelDSPTest \ No newline at end of file diff --git a/test/.idea/codeStyles/Project.xml b/test/.idea/codeStyles/Project.xml deleted file mode 100644 index 4f05c8db..00000000 --- a/test/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - \ No newline at end of file diff --git a/test/.idea/codeStyles/codeStyleConfig.xml b/test/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123c..00000000 --- a/test/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/test/.idea/discord.xml b/test/.idea/discord.xml deleted file mode 100644 index d8e95616..00000000 --- a/test/.idea/discord.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/test/.idea/misc.xml b/test/.idea/misc.xml deleted file mode 100644 index 048657bc..00000000 --- a/test/.idea/misc.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/.idea/modules.xml b/test/.idea/modules.xml deleted file mode 100644 index 51ab9749..00000000 --- a/test/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/test/.idea/test.iml b/test/.idea/test.iml deleted file mode 100644 index f08604bb..00000000 --- a/test/.idea/test.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/test/.idea/vcs.xml b/test/.idea/vcs.xml deleted file mode 100644 index 32328522..00000000 --- a/test/.idea/vcs.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2116d60b..8c312420 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,7 +29,7 @@ add_subdirectory(support) # have to add nanopb before other components for the generator function to work correctly add_subdirectory(../thirdparty thirdparty) -add_subdirectory(../effects/abstract_dsp/ abstract_dsp) +add_subdirectory(../effects effects) add_subdirectory(../plugins/JUCE_static JUCE_static) -add_subdirectory(../firmware firmware) +add_subdirectory(../shrapnel shrapnel) add_subdirectory(integration) diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index 20f47438..97c12eb8 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -1,6 +1,6 @@ add_executable(integration_test) target_sources(integration_test PRIVATE integration_test.cpp) -target_link_libraries(integration_test PRIVATE shrapnel::main) +target_link_libraries(integration_test PRIVATE shrapnel::main_thread) target_link_libraries(integration_test PRIVATE shrapnel::juce_modules) target_link_libraries(integration_test PRIVATE diff --git a/test/integration/integration_test.cpp b/test/integration/integration_test.cpp index 10ace2e9..03538972 100644 --- a/test/integration/integration_test.cpp +++ b/test/integration/integration_test.cpp @@ -29,26 +29,6 @@ #define QUEUE_LEN 4 -extern "C" { - -__attribute__((__noreturn__)) void -_esp_error_check_failed(esp_err_t rc, - const char *file, - int line, - const char *function, - const char *expression) - -{ - assert(false); -} - -void _esp_error_check_failed_without_abort(esp_err_t rc, - const char *file, - int line, - const char *function, - const char *expression){}; -} - namespace shrapnel::midi { template @@ -64,10 +44,10 @@ std::ostream &operator<<(std::ostream &out, const T &message) } // namespace shrapnel::midi -class FakeMidiUart final : public shrapnel::midi::MidiUartBase +class FakeMidiUart final : public shrapnel::midi::MidiUartBase { public: - std::optional get_byte(TickType_t) override + std::optional get_byte(int) override { if(buffer.empty()) return std::nullopt; @@ -123,7 +103,6 @@ class Integration : public ::testing::Test return { send_message_fn, in_queue, - &midi_uart, audio_params, storage, std::move(midi_mapping_storage), @@ -141,7 +120,7 @@ class Integration : public ::testing::Test void pushServerApiMessage(const AppMessage &message) { auto rc = in_queue.send(&message, 0); - ASSERT_THAT(rc, pdPASS); + ASSERT_THAT(rc, shrapnel::queue_error::SUCCESS); } testing::MockFunction send_message; @@ -163,7 +142,7 @@ TEST_F(Integration, NotifiesServerAboutMidiMessages) juce::MidiMessage::noteOn(1, 0, static_cast(1)); pushMidiMessage(note_on_message); - shrapnel::os::Timer::impl::tick(pdMS_TO_TICKS(200)); + shrapnel::os::Timer::impl::tick(os::ms_to_ticks(200)); EXPECT_CALL( send_message, diff --git a/test/support/CMakeLists.txt b/test/support/CMakeLists.txt index 070d3ec1..707d0e44 100644 --- a/test/support/CMakeLists.txt +++ b/test/support/CMakeLists.txt @@ -1,7 +1,3 @@ -add_subdirectory(driver) add_subdirectory(esp_common) add_subdirectory(esp_hw_support) -add_subdirectory(freertos) add_subdirectory(log) -add_subdirectory(nvs) -add_subdirectory(protobuf-c) diff --git a/test/support/driver/CMakeLists.txt b/test/support/driver/CMakeLists.txt deleted file mode 100644 index 78cebdbc..00000000 --- a/test/support/driver/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_library(driver INTERFACE) -add_library(idf::driver ALIAS driver) - -target_include_directories(driver INTERFACE include) diff --git a/test/support/driver/include/driver/gpio.h b/test/support/driver/include/driver/gpio.h deleted file mode 100644 index 303af63f..00000000 --- a/test/support/driver/include/driver/gpio.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -using gpio_num_t = int; diff --git a/test/support/driver/include/driver/uart.h b/test/support/driver/include/driver/uart.h deleted file mode 100644 index 8f69d0ba..00000000 --- a/test/support/driver/include/driver/uart.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -using uart_port_t = int; \ No newline at end of file diff --git a/test/support/esp_common/include/esp_err_backup.h b/test/support/esp_common/include/esp_err_backup.h deleted file mode 100644 index d027e4f9..00000000 --- a/test/support/esp_common/include/esp_err_backup.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -typedef int esp_err_t; - -static inline const char *esp_err_to_name(esp_err_t) { return ""; } - -#ifdef __cplusplus -} // extern "C" -#endif - -#define ESP_OK 0 \ No newline at end of file diff --git a/test/support/freertos/CMakeLists.txt b/test/support/freertos/CMakeLists.txt deleted file mode 100644 index 1ecc5712..00000000 --- a/test/support/freertos/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_library(freertos_config INTERFACE) -target_include_directories(freertos_config INTERFACE include) -target_compile_definitions(freertos_config INTERFACE projCOVERAGE_TEST=0) - -add_subdirectory(FreeRTOS-Kernel) -add_library(idf::freertos ALIAS freertos_kernel) \ No newline at end of file diff --git a/test/support/freertos/FreeRTOS-Kernel b/test/support/freertos/FreeRTOS-Kernel deleted file mode 160000 index f377ce36..00000000 --- a/test/support/freertos/FreeRTOS-Kernel +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f377ce36e7e62a9d237ad8ba2cc6e8b3dd9d63b5 diff --git a/test/support/freertos/include/FreeRTOSConfig.h b/test/support/freertos/include/FreeRTOSConfig.h deleted file mode 100644 index 50df304d..00000000 --- a/test/support/freertos/include/FreeRTOSConfig.h +++ /dev/null @@ -1,183 +0,0 @@ -// clang-format off -/* - * FreeRTOS V202212.00 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * https://www.FreeRTOS.org - * https://github.com/FreeRTOS - * - */ -#ifndef FREERTOS_CONFIG_H -#define FREERTOS_CONFIG_H - -/*----------------------------------------------------------- -* Application specific definitions. -* -* These definitions should be adjusted for your particular hardware and -* application requirements. -* -* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE -* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. See -* https://www.FreeRTOS.org/a00110.html -*----------------------------------------------------------*/ - -#define configUSE_PREEMPTION 1 -#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 -#define configUSE_IDLE_HOOK 1 -#define configUSE_TICK_HOOK 1 -#define configUSE_DAEMON_TASK_STARTUP_HOOK 1 -#define configTICK_RATE_HZ ( 1000 ) /* In this non-real time simulated environment the tick frequency has to be at least a multiple of the Win32 tick frequency, and therefore very slow. */ -#define configMINIMAL_STACK_SIZE ( ( unsigned short ) PTHREAD_STACK_MIN ) /* The stack size being passed is equal to the minimum stack size needed by pthread_create(). */ -#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 65 * 1024 ) ) -#define configMAX_TASK_NAME_LEN ( 12 ) -#define configUSE_TRACE_FACILITY 1 -#define configUSE_16_BIT_TICKS 0 -#define configIDLE_SHOULD_YIELD 1 -#define configUSE_MUTEXES 1 -#define configCHECK_FOR_STACK_OVERFLOW 0 -#define configUSE_RECURSIVE_MUTEXES 1 -#define configQUEUE_REGISTRY_SIZE 20 -#define configUSE_APPLICATION_TASK_TAG 1 -#define configUSE_COUNTING_SEMAPHORES 1 -#define configUSE_ALTERNATIVE_API 0 -#define configUSE_QUEUE_SETS 1 -#define configUSE_TASK_NOTIFICATIONS 1 -#define configSUPPORT_STATIC_ALLOCATION 1 - -/* Software timer related configuration options. The maximum possible task - * priority is configMAX_PRIORITIES - 1. The priority of the timer task is - * deliberately set higher to ensure it is correctly capped back to - * configMAX_PRIORITIES - 1. */ -#define configUSE_TIMERS 1 -#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) -#define configTIMER_QUEUE_LENGTH 20 -#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) - -#define configMAX_PRIORITIES ( 7 ) - -/* Run time stats gathering configuration options. */ -unsigned long ulGetRunTimeCounterValue( void ); /* Prototype of function that returns run time counter. */ -void vConfigureTimerForRunTimeStats( void ); /* Prototype of function that initialises the run time counter. */ -#define configGENERATE_RUN_TIME_STATS 1 - -/* This demo can use of one or more example stats formatting functions. These - * format the raw data provided by the uxTaskGetSystemState() function in to human - * readable ASCII form. See the notes in the implementation of vTaskList() within - * FreeRTOS/Source/tasks.c for limitations. */ -#define configUSE_STATS_FORMATTING_FUNCTIONS 0 - -/* Enables the test whereby a stack larger than the total heap size is - * requested. */ -#define configSTACK_DEPTH_TYPE uint32_t - -/* Set the following definitions to 1 to include the API function, or zero - * to exclude the API function. In most cases the linker will remove unused - * functions anyway. */ -#define INCLUDE_vTaskPrioritySet 1 -#define INCLUDE_uxTaskPriorityGet 1 -#define INCLUDE_vTaskDelete 1 -#define INCLUDE_vTaskCleanUpResources 0 -#define INCLUDE_vTaskSuspend 1 -#define INCLUDE_vTaskDelayUntil 1 -#define INCLUDE_vTaskDelay 1 -#define INCLUDE_uxTaskGetStackHighWaterMark 1 -#define INCLUDE_uxTaskGetStackHighWaterMark2 1 -#define INCLUDE_xTaskGetSchedulerState 1 -#define INCLUDE_xTimerGetTimerDaemonTaskHandle 1 -#define INCLUDE_xTaskGetIdleTaskHandle 1 -#define INCLUDE_xTaskGetHandle 1 -#define INCLUDE_eTaskGetState 1 -#define INCLUDE_xSemaphoreGetMutexHolder 1 -#define INCLUDE_xTimerPendFunctionCall 1 -#define INCLUDE_xTaskAbortDelay 1 - -#define configINCLUDE_MESSAGE_BUFFER_AMP_DEMO 0 -#if ( configINCLUDE_MESSAGE_BUFFER_AMP_DEMO == 1 ) - extern void vGenerateCoreBInterrupt( void * xUpdatedMessageBuffer ); - #define sbSEND_COMPLETED( pxStreamBuffer ) vGenerateCoreBInterrupt( pxStreamBuffer ) -#endif /* configINCLUDE_MESSAGE_BUFFER_AMP_DEMO */ - -extern void vAssertCalled( const char * const pcFileName, - unsigned long ulLine ); - -/* projCOVERAGE_TEST should be defined on the command line so this file can be - * used with multiple project configurations. If it is - */ -#ifndef projCOVERAGE_TEST - #error projCOVERAGE_TEST should be defined to 1 or 0 on the command line. -#endif - -#if ( projCOVERAGE_TEST == 1 ) - -/* Insert NOPs in empty decision paths to ensure both true and false paths - * are being tested. */ - #define mtCOVERAGE_TEST_MARKER() __asm volatile ( "NOP" ) - -/* Ensure the tick count overflows during the coverage test. */ - #define configINITIAL_TICK_COUNT 0xffffd800UL - -/* Allows tests of trying to allocate more than the heap has free. */ - #define configUSE_MALLOC_FAILED_HOOK 0 - -/* To test builds that remove the static qualifier for debug builds. */ - #define portREMOVE_STATIC_QUALIFIER -#else /* if ( projCOVERAGE_TEST == 1 ) */ - -/* It is a good idea to define configASSERT() while developing. configASSERT() - * uses the same semantics as the standard C assert() macro. Don't define - * configASSERT() when performing code coverage tests though, as it is not - * intended to asserts() to fail, some some code is intended not to run if no - * errors are present. */ - #define configASSERT( x ) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ ) - - #define configUSE_MALLOC_FAILED_HOOK 1 - -/* Include the FreeRTOS+Trace FreeRTOS trace macro definitions. */ - // #include "trcRecorder.h" -#endif /* if ( projCOVERAGE_TEST == 1 ) */ - -/* networking definitions */ -#define configMAC_ISR_SIMULATOR_PRIORITY ( configMAX_PRIORITIES - 1 ) - -/* Prototype for the function used to print out. In this case it prints to the - * console before the network is connected then a UDP port after the network has - * connected. */ -extern void vLoggingPrintf( const char * pcFormatString, - ... ); - -/* Set to 1 to print out debug messages. If ipconfigHAS_DEBUG_PRINTF is set to - * 1 then FreeRTOS_debug_printf should be defined to the function used to print - * out the debugging messages. */ -#define ipconfigHAS_DEBUG_PRINTF 1 -#if ( ipconfigHAS_DEBUG_PRINTF == 1 ) - #define FreeRTOS_debug_printf( X ) vLoggingPrintf X -#endif - -/* Set to 1 to print out non debugging messages, for example the output of the - * FreeRTOS_netstat() command, and ping replies. If ipconfigHAS_PRINTF is set to 1 - * then FreeRTOS_printf should be set to the function used to print out the - * messages. */ -#define ipconfigHAS_PRINTF 0 -#if ( ipconfigHAS_PRINTF == 1 ) - #define FreeRTOS_printf( X ) vLoggingPrintf X -#endif -#endif /* FREERTOS_CONFIG_H */ - -// clang-format on \ No newline at end of file diff --git a/test/support/nvs/CMakeLists.txt b/test/support/nvs/CMakeLists.txt deleted file mode 100644 index 82c783ea..00000000 --- a/test/support/nvs/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_library(idf_nvs_flash INTERFACE) -add_library(idf::nvs_flash ALIAS idf_nvs_flash) - -target_include_directories(idf_nvs_flash INTERFACE include) - -target_link_libraries(idf_nvs_flash INTERFACE idf::esp_common) \ No newline at end of file diff --git a/test/support/nvs/include/nvs.h b/test/support/nvs/include/nvs.h deleted file mode 100644 index 9987f2c8..00000000 --- a/test/support/nvs/include/nvs.h +++ /dev/null @@ -1,786 +0,0 @@ -/* -* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD -* -* SPDX-License-Identifier: Apache-2.0 -*/ -#ifndef ESP_NVS_H -#define ESP_NVS_H - -#include -#include -#include -#include "esp_attr.h" -#include "esp_err.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** -* Opaque pointer type representing non-volatile storage handle -*/ -typedef uint32_t nvs_handle_t; - -/* -* Pre-IDF V4.0 uses nvs_handle, so leaving the original typedef here for compatibility. -*/ -typedef nvs_handle_t nvs_handle IDF_DEPRECATED("Replace with nvs_handle_t"); - -#define ESP_ERR_NVS_BASE 0x1100 /*!< Starting number of error codes */ -#define ESP_ERR_NVS_NOT_INITIALIZED (ESP_ERR_NVS_BASE + 0x01) /*!< The storage driver is not initialized */ -#define ESP_ERR_NVS_NOT_FOUND (ESP_ERR_NVS_BASE + 0x02) /*!< A requested entry couldn't be found or namespace doesn’t exist yet and mode is NVS_READONLY */ -#define ESP_ERR_NVS_TYPE_MISMATCH (ESP_ERR_NVS_BASE + 0x03) /*!< The type of set or get operation doesn't match the type of value stored in NVS */ -#define ESP_ERR_NVS_READ_ONLY (ESP_ERR_NVS_BASE + 0x04) /*!< Storage handle was opened as read only */ -#define ESP_ERR_NVS_NOT_ENOUGH_SPACE (ESP_ERR_NVS_BASE + 0x05) /*!< There is not enough space in the underlying storage to save the value */ -#define ESP_ERR_NVS_INVALID_NAME (ESP_ERR_NVS_BASE + 0x06) /*!< Namespace name doesn’t satisfy constraints */ -#define ESP_ERR_NVS_INVALID_HANDLE (ESP_ERR_NVS_BASE + 0x07) /*!< Handle has been closed or is NULL */ -#define ESP_ERR_NVS_REMOVE_FAILED (ESP_ERR_NVS_BASE + 0x08) /*!< The value wasn’t updated because flash write operation has failed. The value was written however, and update will be finished after re-initialization of nvs, provided that flash operation doesn’t fail again. */ -#define ESP_ERR_NVS_KEY_TOO_LONG (ESP_ERR_NVS_BASE + 0x09) /*!< Key name is too long */ -#define ESP_ERR_NVS_PAGE_FULL (ESP_ERR_NVS_BASE + 0x0a) /*!< Internal error; never returned by nvs API functions */ -#define ESP_ERR_NVS_INVALID_STATE (ESP_ERR_NVS_BASE + 0x0b) /*!< NVS is in an inconsistent state due to a previous error. Call nvs_flash_init and nvs_open again, then retry. */ -#define ESP_ERR_NVS_INVALID_LENGTH (ESP_ERR_NVS_BASE + 0x0c) /*!< String or blob length is not sufficient to store data */ -#define ESP_ERR_NVS_NO_FREE_PAGES (ESP_ERR_NVS_BASE + 0x0d) /*!< NVS partition doesn't contain any empty pages. This may happen if NVS partition was truncated. Erase the whole partition and call nvs_flash_init again. */ -#define ESP_ERR_NVS_VALUE_TOO_LONG (ESP_ERR_NVS_BASE + 0x0e) /*!< Value doesn't fit into the entry or string or blob length is longer than supported by the implementation */ -#define ESP_ERR_NVS_PART_NOT_FOUND (ESP_ERR_NVS_BASE + 0x0f) /*!< Partition with specified name is not found in the partition table */ - -#define ESP_ERR_NVS_NEW_VERSION_FOUND (ESP_ERR_NVS_BASE + 0x10) /*!< NVS partition contains data in new format and cannot be recognized by this version of code */ -#define ESP_ERR_NVS_XTS_ENCR_FAILED (ESP_ERR_NVS_BASE + 0x11) /*!< XTS encryption failed while writing NVS entry */ -#define ESP_ERR_NVS_XTS_DECR_FAILED (ESP_ERR_NVS_BASE + 0x12) /*!< XTS decryption failed while reading NVS entry */ -#define ESP_ERR_NVS_XTS_CFG_FAILED (ESP_ERR_NVS_BASE + 0x13) /*!< XTS configuration setting failed */ -#define ESP_ERR_NVS_XTS_CFG_NOT_FOUND (ESP_ERR_NVS_BASE + 0x14) /*!< XTS configuration not found */ -#define ESP_ERR_NVS_ENCR_NOT_SUPPORTED (ESP_ERR_NVS_BASE + 0x15) /*!< NVS encryption is not supported in this version */ -#define ESP_ERR_NVS_KEYS_NOT_INITIALIZED (ESP_ERR_NVS_BASE + 0x16) /*!< NVS key partition is uninitialized */ -#define ESP_ERR_NVS_CORRUPT_KEY_PART (ESP_ERR_NVS_BASE + 0x17) /*!< NVS key partition is corrupt */ -#define ESP_ERR_NVS_WRONG_ENCRYPTION (ESP_ERR_NVS_BASE + 0x19) /*!< NVS partition is marked as encrypted with generic flash encryption. This is forbidden since the NVS encryption works differently. */ - -#define ESP_ERR_NVS_CONTENT_DIFFERS (ESP_ERR_NVS_BASE + 0x18) /*!< Internal error; never returned by nvs API functions. NVS key is different in comparison */ - -#define NVS_DEFAULT_PART_NAME "nvs" /*!< Default partition name of the NVS partition in the partition table */ - -#define NVS_PART_NAME_MAX_SIZE 16 /*!< maximum length of partition name (excluding null terminator) */ -#define NVS_KEY_NAME_MAX_SIZE 16 /*!< Maximum length of NVS key name (including null terminator) */ -#define NVS_NS_NAME_MAX_SIZE NVS_KEY_NAME_MAX_SIZE /*!< Maximum length of NVS namespace name (including null terminator) */ - -/** -* @brief Mode of opening the non-volatile storage -*/ -typedef enum { - NVS_READONLY, /*!< Read only */ - NVS_READWRITE /*!< Read and write */ -} nvs_open_mode_t; - -/* -* Pre-IDF V4.0 uses nvs_open_mode, so leaving the original typedef here for compatibility. -*/ -typedef nvs_open_mode_t nvs_open_mode IDF_DEPRECATED("Replace with nvs_open_mode_t"); - - -/** -* @brief Types of variables -* -*/ -typedef enum { - NVS_TYPE_U8 = 0x01, /*!< Type uint8_t */ - NVS_TYPE_I8 = 0x11, /*!< Type int8_t */ - NVS_TYPE_U16 = 0x02, /*!< Type uint16_t */ - NVS_TYPE_I16 = 0x12, /*!< Type int16_t */ - NVS_TYPE_U32 = 0x04, /*!< Type uint32_t */ - NVS_TYPE_I32 = 0x14, /*!< Type int32_t */ - NVS_TYPE_U64 = 0x08, /*!< Type uint64_t */ - NVS_TYPE_I64 = 0x18, /*!< Type int64_t */ - NVS_TYPE_STR = 0x21, /*!< Type string */ - NVS_TYPE_BLOB = 0x42, /*!< Type blob */ - NVS_TYPE_ANY = 0xff /*!< Must be last */ -} nvs_type_t; - -/** -* @brief information about entry obtained from nvs_entry_info function -*/ -typedef struct { - char namespace_name[NVS_NS_NAME_MAX_SIZE]; /*!< Namespace to which key-value belong */ - char key[NVS_KEY_NAME_MAX_SIZE]; /*!< Key of stored key-value pair */ - nvs_type_t type; /*!< Type of stored key-value pair */ -} nvs_entry_info_t; - -/** -* Opaque pointer type representing iterator to nvs entries -*/ -typedef struct nvs_opaque_iterator_t *nvs_iterator_t; - -/** -* @brief Open non-volatile storage with a given namespace from the default NVS partition -* -* Multiple internal ESP-IDF and third party application modules can store -* their key-value pairs in the NVS module. In order to reduce possible -* conflicts on key names, each module can use its own namespace. -* The default NVS partition is the one that is labelled "nvs" in the partition -* table. -* -* @param[in] namespace_name Namespace name. Maximum length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty. -* @param[in] open_mode NVS_READWRITE or NVS_READONLY. If NVS_READONLY, will -* open a handle for reading only. All write requests will -* be rejected for this handle. -* @param[out] out_handle If successful (return code is zero), handle will be -* returned in this argument. -* -* @return -* - ESP_OK if storage handle was opened successfully -* - ESP_FAIL if there is an internal error; most likely due to corrupted -* NVS partition (only if NVS assertion checks are disabled) -* - ESP_ERR_NVS_NOT_INITIALIZED if the storage driver is not initialized -* - ESP_ERR_NVS_PART_NOT_FOUND if the partition with label "nvs" is not found -* - ESP_ERR_NVS_NOT_FOUND id namespace doesn't exist yet and -* mode is NVS_READONLY -* - ESP_ERR_NVS_INVALID_NAME if namespace name doesn't satisfy constraints -* - ESP_ERR_NO_MEM in case memory could not be allocated for the internal structures -* - ESP_ERR_NVS_NOT_ENOUGH_SPACE if there is no space for a new entry or there are too many different -* namespaces (maximum allowed different namespaces: 254) -* - ESP_ERR_NOT_ALLOWED if the NVS partition is read-only and mode is NVS_READWRITE -* - ESP_ERR_INVALID_ARG if out_handle is equal to NULL -* - other error codes from the underlying storage driver -*/ -esp_err_t nvs_open(const char* namespace_name, nvs_open_mode_t open_mode, nvs_handle_t *out_handle); - -/** -* @brief Open non-volatile storage with a given namespace from specified partition -* -* The behaviour is same as nvs_open() API. However this API can operate on a specified NVS -* partition instead of default NVS partition. Note that the specified partition must be registered -* with NVS using nvs_flash_init_partition() API. -* -* @param[in] part_name Label (name) of the partition of interest for object read/write/erase -* @param[in] namespace_name Namespace name. Maximum length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty. -* @param[in] open_mode NVS_READWRITE or NVS_READONLY. If NVS_READONLY, will -* open a handle for reading only. All write requests will -* be rejected for this handle. -* @param[out] out_handle If successful (return code is zero), handle will be -* returned in this argument. -* -* @return -* - ESP_OK if storage handle was opened successfully -* - ESP_FAIL if there is an internal error; most likely due to corrupted -* NVS partition (only if NVS assertion checks are disabled) -* - ESP_ERR_NVS_NOT_INITIALIZED if the storage driver is not initialized -* - ESP_ERR_NVS_PART_NOT_FOUND if the partition with specified name is not found -* - ESP_ERR_NVS_NOT_FOUND id namespace doesn't exist yet and -* mode is NVS_READONLY -* - ESP_ERR_NVS_INVALID_NAME if namespace name doesn't satisfy constraints -* - ESP_ERR_NO_MEM in case memory could not be allocated for the internal structures -* - ESP_ERR_NVS_NOT_ENOUGH_SPACE if there is no space for a new entry or there are too many different -* namespaces (maximum allowed different namespaces: 254) -* - ESP_ERR_NOT_ALLOWED if the NVS partition is read-only and mode is NVS_READWRITE -* - ESP_ERR_INVALID_ARG if out_handle is equal to NULL -* - other error codes from the underlying storage driver -*/ -esp_err_t nvs_open_from_partition(const char *part_name, const char* namespace_name, nvs_open_mode_t open_mode, nvs_handle_t *out_handle); - -/**@{*/ -/** -* @brief set int8_t value for given key -* -* Set value for the key, given its name. Note that the actual storage will not be updated -* until \c nvs_commit is called. Regardless whether key-value pair is created or updated, -* function always requires at least one nvs available entry. See \c nvs_get_stats . -* After create type of operation, the number of available entries is decreased by one. -* After update type of operation, the number of available entries remains the same. -* -* @param[in] handle Handle obtained from nvs_open function. -* Handles that were opened read only cannot be used. -* @param[in] key Key name. Maximum length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty. -* @param[in] value The value to set. -* -* @return -* - ESP_OK if value was set successfully -* - ESP_FAIL if there is an internal error; most likely due to corrupted -* NVS partition (only if NVS assertion checks are disabled) -* - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL -* - ESP_ERR_NVS_READ_ONLY if storage handle was opened as read only -* - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints -* - ESP_ERR_NVS_NOT_ENOUGH_SPACE if there is not enough space in the -* underlying storage to save the value -* - ESP_ERR_NVS_REMOVE_FAILED if the value wasn't updated because flash -* write operation has failed. The value was written however, and -* update will be finished after re-initialization of nvs, provided that -* flash operation doesn't fail again. -*/ -esp_err_t nvs_set_i8 (nvs_handle_t handle, const char* key, int8_t value); - -/** -* @brief set uint8_t value for given key -* -* This function is the same as \c nvs_set_i8 except for the data type. -*/ -esp_err_t nvs_set_u8 (nvs_handle_t handle, const char* key, uint8_t value); - -/** -* @brief set int16_t value for given key -* -* This function is the same as \c nvs_set_i8 except for the data type. -*/ -esp_err_t nvs_set_i16 (nvs_handle_t handle, const char* key, int16_t value); - -/** -* @brief set uint16_t value for given key -* -* This function is the same as \c nvs_set_i8 except for the data type. -*/ -esp_err_t nvs_set_u16 (nvs_handle_t handle, const char* key, uint16_t value); - -/** -* @brief set int32_t value for given key -* -* This function is the same as \c nvs_set_i8 except for the data type. -*/ -esp_err_t nvs_set_i32 (nvs_handle_t handle, const char* key, int32_t value); - -/** -* @brief set uint32_t value for given key -* -* This function is the same as \c nvs_set_i8 except for the data type. -*/ -esp_err_t nvs_set_u32 (nvs_handle_t handle, const char* key, uint32_t value); - -/** -* @brief set int64_t value for given key -* -* This function is the same as \c nvs_set_i8 except for the data type. -*/ -esp_err_t nvs_set_i64 (nvs_handle_t handle, const char* key, int64_t value); - -/** -* @brief set uint64_t value for given key -* -* This function is the same as \c nvs_set_i8 except for the data type. -*/ -esp_err_t nvs_set_u64 (nvs_handle_t handle, const char* key, uint64_t value); - -/** -* @brief set string for given key -* -* Sets string value for the key. Function requires whole space for new data to be available -* as contiguous entries in same nvs page. Operation consumes 1 overhead entry and 1 entry per -* each 32 characters of new string including zero character to be set. In case of value update -* for existing key, entries occupied by the previous value and overhead entry are returned to -* the pool of available entries. -* Note that storage of long string values can fail due to fragmentation of nvs pages even if -* \c available_entries returned by \c nvs_get_stats suggests enough overall space available. -* Note that the underlying storage will not be updated until \c nvs_commit is called. -* -* -* @param[in] handle Handle obtained from nvs_open function. -* Handles that were opened read only cannot be used. -* @param[in] key Key name. Maximum length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty. -* @param[in] value The value to set. -* For strings, the maximum length (including null character) is -* 4000 bytes, if there is one complete page free for writing. -* This decreases, however, if the free space is fragmented. -* -* @return -* - ESP_OK if value was set successfully -* - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL -* - ESP_ERR_NVS_READ_ONLY if storage handle was opened as read only -* - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints -* - ESP_ERR_NVS_NOT_ENOUGH_SPACE if there is not enough space in the -* underlying storage to save the value -* - ESP_ERR_NVS_REMOVE_FAILED if the value wasn't updated because flash -* write operation has failed. The value was written however, and -* update will be finished after re-initialization of nvs, provided that -* flash operation doesn't fail again. -* - ESP_ERR_NVS_VALUE_TOO_LONG if the string value is too long -*/ -esp_err_t nvs_set_str (nvs_handle_t handle, const char* key, const char* value); -/**@}*/ - -/** -* @brief set variable length binary value for given key -* -* Sets variable length binary value for the key. Function uses 2 overhead and 1 entry -* per each 32 bytes of new data from the pool of available entries. See \c nvs_get_stats . -* In case of value update for existing key, space occupied by the existing value and 2 overhead entries -* are returned to the pool of available entries. -* Note that the underlying storage will not be updated until \c nvs_commit is called. -* -* @param[in] handle Handle obtained from nvs_open function. -* Handles that were opened read only cannot be used. -* @param[in] key Key name. Maximum length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty. -* @param[in] value The value to set. -* @param[in] length length of binary value to set, in bytes; Maximum length is -* 508000 bytes or (97.6% of the partition size - 4000) bytes -* whichever is lower. -* -* @return -* - ESP_OK if value was set successfully -* - ESP_FAIL if there is an internal error; most likely due to corrupted -* NVS partition (only if NVS assertion checks are disabled) -* - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL -* - ESP_ERR_NVS_READ_ONLY if storage handle was opened as read only -* - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints -* - ESP_ERR_NVS_NOT_ENOUGH_SPACE if there is not enough space in the -* underlying storage to save the value -* - ESP_ERR_NVS_REMOVE_FAILED if the value wasn't updated because flash -* write operation has failed. The value was written however, and -* update will be finished after re-initialization of nvs, provided that -* flash operation doesn't fail again. -* - ESP_ERR_NVS_VALUE_TOO_LONG if the value is too long -*/ -esp_err_t nvs_set_blob(nvs_handle_t handle, const char* key, const void* value, size_t length); - -/**@{*/ -/** -* @brief get int8_t value for given key -* -* These functions retrieve value for the key, given its name. If \c key does not -* exist, or the requested variable type doesn't match the type which was used -* when setting a value, an error is returned. -* -* In case of any error, out_value is not modified. -* -* \c out_value has to be a pointer to an already allocated variable of the given type. -* -* \code{c} -* // Example of using nvs_get_i32: -* int32_t max_buffer_size = 4096; // default value -* esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size); -* assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND); -* // if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still -* // have its default value. -* -* \endcode -* -* @param[in] handle Handle obtained from nvs_open function. -* @param[in] key Key name. Maximum length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty. -* @param out_value Pointer to the output value. -* May be NULL for nvs_get_str and nvs_get_blob, in this -* case required length will be returned in length argument. -* -* @return -* - ESP_OK if the value was retrieved successfully -* - ESP_FAIL if there is an internal error; most likely due to corrupted -* NVS partition (only if NVS assertion checks are disabled) -* - ESP_ERR_NVS_NOT_FOUND if the requested key doesn't exist -* - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL -* - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints -* - ESP_ERR_NVS_INVALID_LENGTH if length is not sufficient to store data -*/ -esp_err_t nvs_get_i8 (nvs_handle_t handle, const char* key, int8_t* out_value); - -/** -* @brief get uint8_t value for given key -* -* This function is the same as \c nvs_get_i8 except for the data type. -*/ -esp_err_t nvs_get_u8 (nvs_handle_t handle, const char* key, uint8_t* out_value); - -/** -* @brief get int16_t value for given key -* -* This function is the same as \c nvs_get_i8 except for the data type. -*/ -esp_err_t nvs_get_i16 (nvs_handle_t handle, const char* key, int16_t* out_value); - -/** -* @brief get uint16_t value for given key -* -* This function is the same as \c nvs_get_i8 except for the data type. -*/ -esp_err_t nvs_get_u16 (nvs_handle_t handle, const char* key, uint16_t* out_value); - -/** -* @brief get int32_t value for given key -* -* This function is the same as \c nvs_get_i8 except for the data type. -*/ -esp_err_t nvs_get_i32 (nvs_handle_t handle, const char* key, int32_t* out_value); - -/** -* @brief get uint32_t value for given key -* -* This function is the same as \c nvs_get_i8 except for the data type. -*/ -esp_err_t nvs_get_u32 (nvs_handle_t handle, const char* key, uint32_t* out_value); - -/** -* @brief get int64_t value for given key -* -* This function is the same as \c nvs_get_i8 except for the data type. -*/ -esp_err_t nvs_get_i64 (nvs_handle_t handle, const char* key, int64_t* out_value); - -/** -* @brief get uint64_t value for given key -* -* This function is the same as \c nvs_get_i8 except for the data type. -*/ -esp_err_t nvs_get_u64 (nvs_handle_t handle, const char* key, uint64_t* out_value); -/**@}*/ - -/**@{*/ -/** -* @brief get string value for given key -* -* These functions retrieve the data of an entry, given its key. If key does not -* exist, or the requested variable type doesn't match the type which was used -* when setting a value, an error is returned. -* -* In case of any error, out_value is not modified. -* -* All functions expect out_value to be a pointer to an already allocated variable -* of the given type. -* -* nvs_get_str and nvs_get_blob functions support WinAPI-style length queries. -* To get the size necessary to store the value, call nvs_get_str or nvs_get_blob -* with zero out_value and non-zero pointer to length. Variable pointed to -* by length argument will be set to the required length. For nvs_get_str, -* this length includes the zero terminator. When calling nvs_get_str and -* nvs_get_blob with non-zero out_value, length has to be non-zero and has to -* point to the length available in out_value. -* It is suggested that nvs_get/set_str is used for zero-terminated C strings, and -* nvs_get/set_blob used for arbitrary data structures. -* -* \code{c} -* // Example (without error checking) of using nvs_get_str to get a string into dynamic array: -* size_t required_size; -* nvs_get_str(my_handle, "server_name", NULL, &required_size); -* char* server_name = malloc(required_size); -* nvs_get_str(my_handle, "server_name", server_name, &required_size); -* -* // Example (without error checking) of using nvs_get_blob to get a binary data -* into a static array: -* uint8_t mac_addr[6]; -* size_t size = sizeof(mac_addr); -* nvs_get_blob(my_handle, "dst_mac_addr", mac_addr, &size); -* \endcode -* -* @param[in] handle Handle obtained from nvs_open function. -* @param[in] key Key name. Maximum length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty. -* @param[out] out_value Pointer to the output value. -* May be NULL for nvs_get_str and nvs_get_blob, in this -* case required length will be returned in length argument. -* @param[inout] length A non-zero pointer to the variable holding the length of out_value. -* In case out_value a zero, will be set to the length -* required to hold the value. In case out_value is not -* zero, will be set to the actual length of the value -* written. For nvs_get_str this includes zero terminator. -* -* @return -* - ESP_OK if the value was retrieved successfully -* - ESP_FAIL if there is an internal error; most likely due to corrupted -* NVS partition (only if NVS assertion checks are disabled) -* - ESP_ERR_NVS_NOT_FOUND if the requested key doesn't exist -* - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL -* - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints -* - ESP_ERR_NVS_INVALID_LENGTH if \c length is not sufficient to store data -*/ -esp_err_t nvs_get_str (nvs_handle_t handle, const char* key, char* out_value, size_t* length); - -/** -* @brief get blob value for given key -* -* This function behaves the same as \c nvs_get_str, except for the data type. -*/ -esp_err_t nvs_get_blob(nvs_handle_t handle, const char* key, void* out_value, size_t* length); -/**@}*/ - -/** -* @brief Lookup key-value pair with given key name. -* -* Note that function may indicate both existence of the key as well as the data type of NVS entry if it is found. -* -* @param[in] handle Storage handle obtained with nvs_open. -* @param[in] key Key name. Maximum length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty. -* @param[out] out_type Pointer to the output variable populated with data type of NVS entry in case key was found. -* May be NULL, respective data type is then not provided. -* @return -* - ESP_OK if NVS entry for key provided was found -* - ESP_ERR_NVS_NOT_FOUND if the requested key doesn't exist -* - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL -* - ESP_FAIL if there is an internal error; most likely due to corrupted -* NVS partition (only if NVS assertion checks are disabled) -* - other error codes from the underlying storage driver -*/ -esp_err_t nvs_find_key(nvs_handle_t handle, const char* key, nvs_type_t* out_type); - -/** -* @brief Erase key-value pair with given key name. -* -* Note that actual storage may not be updated until nvs_commit function is called. -* -* @param[in] handle Storage handle obtained with nvs_open. -* Handles that were opened read only cannot be used. -* -* @param[in] key Key name. Maximum length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty. -* -* @return -* - ESP_OK if erase operation was successful -* - ESP_FAIL if there is an internal error; most likely due to corrupted -* NVS partition (only if NVS assertion checks are disabled) -* - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL -* - ESP_ERR_NVS_READ_ONLY if handle was opened as read only -* - ESP_ERR_NVS_NOT_FOUND if the requested key doesn't exist -* - other error codes from the underlying storage driver -*/ -esp_err_t nvs_erase_key(nvs_handle_t handle, const char* key); - -/** -* @brief Erase all key-value pairs in a namespace -* -* Note that actual storage may not be updated until nvs_commit function is called. -* -* @param[in] handle Storage handle obtained with nvs_open. -* Handles that were opened read only cannot be used. -* -* @return -* - ESP_OK if erase operation was successful -* - ESP_FAIL if there is an internal error; most likely due to corrupted -* NVS partition (only if NVS assertion checks are disabled) -* - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL -* - ESP_ERR_NVS_READ_ONLY if handle was opened as read only -* - other error codes from the underlying storage driver -*/ -esp_err_t nvs_erase_all(nvs_handle_t handle); - -/** -* @brief Write any pending changes to non-volatile storage -* -* After setting any values, nvs_commit() must be called to ensure changes are written -* to non-volatile storage. Individual implementations may write to storage at other times, -* but this is not guaranteed. -* -* @param[in] handle Storage handle obtained with nvs_open. -* Handles that were opened read only cannot be used. -* -* @return -* - ESP_OK if the changes have been written successfully -* - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL -* - other error codes from the underlying storage driver -*/ -esp_err_t nvs_commit(nvs_handle_t handle); - -/** -* @brief Close the storage handle and free any allocated resources -* -* This function should be called for each handle opened with nvs_open once -* the handle is not in use any more. Closing the handle may not automatically -* write the changes to nonvolatile storage. This has to be done explicitly using -* nvs_commit function. -* Once this function is called on a handle, the handle should no longer be used. -* -* @param[in] handle Storage handle to close -*/ -void nvs_close(nvs_handle_t handle); - -/** -* @note Info about storage space NVS. -*/ -typedef struct { - size_t used_entries; /**< Number of used entries. */ - size_t free_entries; /**< Number of free entries. It includes also reserved entries. */ - size_t available_entries; /**< Number of entries available for data storage. */ - size_t total_entries; /**< Number of all entries. */ - size_t namespace_count; /**< Number of namespaces. */ -} nvs_stats_t; - -/** -* @brief Fill structure nvs_stats_t. It provides info about memory used by NVS. -* -* This function calculates the number of used entries, free entries, available entries, total entries -* and number of namespaces in partition. -* -* \code{c} -* // Example of nvs_get_stats() to get overview of actual statistics of data entries : -* nvs_stats_t nvs_stats; -* nvs_get_stats(NULL, &nvs_stats); -* printf("Count: UsedEntries = (%lu), FreeEntries = (%lu), AvailableEntries = (%lu), AllEntries = (%lu)\n", - nvs_stats.used_entries, nvs_stats.free_entries, nvs_stats.available_entries, nvs_stats.total_entries); -* \endcode -* -* @param[in] part_name Partition name NVS in the partition table. -* If pass a NULL than will use NVS_DEFAULT_PART_NAME ("nvs"). -* -* @param[out] nvs_stats Returns filled structure nvs_states_t. -* It provides info about used memory the partition. -* -* -* @return -* - ESP_OK if the changes have been written successfully. -* Return param nvs_stats will be filled. -* - ESP_ERR_NVS_PART_NOT_FOUND if the partition with label "name" is not found. -* Return param nvs_stats will be filled 0. -* - ESP_ERR_NVS_NOT_INITIALIZED if the storage driver is not initialized. -* Return param nvs_stats will be filled 0. -* - ESP_ERR_INVALID_ARG if nvs_stats is equal to NULL. -* - ESP_ERR_INVALID_STATE if there is page with the status of INVALID. -* Return param nvs_stats will be filled not with correct values because -* not all pages will be counted. Counting will be interrupted at the first INVALID page. -*/ -esp_err_t nvs_get_stats(const char *part_name, nvs_stats_t *nvs_stats); - -/** -* @brief Calculate all entries in a namespace. -* -* An entry represents the smallest storage unit in NVS. -* Strings and blobs may occupy more than one entry. -* Note that to find out the total number of entries occupied by the namespace, -* add one to the returned value used_entries (if err is equal to ESP_OK). -* Because the name space entry takes one entry. -* -* \code{c} -* // Example of nvs_get_used_entry_count() to get amount of all key-value pairs in one namespace: -* nvs_handle_t handle; -* nvs_open("namespace1", NVS_READWRITE, &handle); -* ... -* size_t used_entries; -* size_t total_entries_namespace; -* if(nvs_get_used_entry_count(handle, &used_entries) == ESP_OK){ -* // the total number of entries occupied by the namespace -* total_entries_namespace = used_entries + 1; -* } -* \endcode -* -* @param[in] handle Handle obtained from nvs_open function. -* -* @param[out] used_entries Returns amount of used entries from a namespace. -* -* -* @return -* - ESP_OK if the changes have been written successfully. -* Return param used_entries will be filled valid value. -* - ESP_ERR_NVS_NOT_INITIALIZED if the storage driver is not initialized. -* Return param used_entries will be filled 0. -* - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL. -* Return param used_entries will be filled 0. -* - ESP_ERR_INVALID_ARG if used_entries is equal to NULL. -* - Other error codes from the underlying storage driver. -* Return param used_entries will be filled 0. -*/ -esp_err_t nvs_get_used_entry_count(nvs_handle_t handle, size_t* used_entries); - -/** -* @brief Create an iterator to enumerate NVS entries based on one or more parameters -* -* \code{c} -* // Example of listing all the key-value pairs of any type under specified partition and namespace -* nvs_iterator_t it = NULL; -* esp_err_t res = nvs_entry_find(, , NVS_TYPE_ANY, &it); -* while(res == ESP_OK) { -* nvs_entry_info_t info; -* nvs_entry_info(it, &info); // Can omit error check if parameters are guaranteed to be non-NULL -* printf("key '%s', type '%d' \n", info.key, info.type); -* res = nvs_entry_next(&it); -* } -* nvs_release_iterator(it); -* \endcode -* -* @param[in] part_name Partition name -* -* @param[in] namespace_name Set this value if looking for entries with -* a specific namespace. Pass NULL otherwise. -* -* @param[in] type One of nvs_type_t values. -* -* @param[out] output_iterator -* Set to a valid iterator to enumerate all the entries found. -* Set to NULL if no entry for specified criteria was found. -* If any other error except ESP_ERR_INVALID_ARG occurs, \c output_iterator is NULL, too. -* If ESP_ERR_INVALID_ARG occurs, \c output_iterator is not changed. -* If a valid iterator is obtained through this function, it has to be released -* using \c nvs_release_iterator when not used any more, unless ESP_ERR_INVALID_ARG is returned. -* -* @return -* - ESP_OK if no internal error or programming error occurred. -* - ESP_ERR_NVS_NOT_FOUND if no element of specified criteria has been found. -* - ESP_ERR_NO_MEM if memory has been exhausted during allocation of internal structures. -* - ESP_ERR_INVALID_ARG if any of the parameters is NULL. -* Note: don't release \c output_iterator in case ESP_ERR_INVALID_ARG has been returned -*/ -esp_err_t nvs_entry_find(const char *part_name, - const char *namespace_name, - nvs_type_t type, - nvs_iterator_t *output_iterator); - -/** -* @brief Create an iterator to enumerate NVS entries based on a handle and type -* -* \code{c} -* // Example of listing all the key-value pairs of any type under specified handle (which defines a partition and namespace) -* nvs_iterator_t it = NULL; -* esp_err_t res = nvs_entry_find_in_handle(, NVS_TYPE_ANY, &it); -* while(res == ESP_OK) { -* nvs_entry_info_t info; -* nvs_entry_info(it, &info); // Can omit error check if parameters are guaranteed to be non-NULL -* printf("key '%s', type '%d' \n", info.key, info.type); -* res = nvs_entry_next(&it); -* } -* nvs_release_iterator(it); -* \endcode -* -* @param[in] handle Handle obtained from nvs_open function. -* -* @param[in] type One of nvs_type_t values. -* -* @param[out] output_iterator -* Set to a valid iterator to enumerate all the entries found. -* Set to NULL if no entry for specified criteria was found. -* If any other error except ESP_ERR_INVALID_ARG occurs, \c output_iterator is NULL, too. -* If ESP_ERR_INVALID_ARG occurs, \c output_iterator is not changed. -* If a valid iterator is obtained through this function, it has to be released -* using \c nvs_release_iterator when not used any more, unless ESP_ERR_INVALID_ARG is returned. -* -* @return -* - ESP_OK if no internal error or programming error occurred. -* - ESP_ERR_NVS_NOT_FOUND if no element of specified criteria has been found. -* - ESP_ERR_NO_MEM if memory has been exhausted during allocation of internal structures. -* - ESP_ERR_NVS_INVALID_HANDLE if unknown handle was specified. -* - ESP_ERR_INVALID_ARG if output_iterator parameter is NULL. -* Note: don't release \c output_iterator in case ESP_ERR_INVALID_ARG has been returned -*/ -esp_err_t nvs_entry_find_in_handle(nvs_handle_t handle, nvs_type_t type, nvs_iterator_t *output_iterator); - -/** -* @brief Advances the iterator to next item matching the iterator criteria. -* -* Note that any copies of the iterator will be invalid after this call. -* -* @param[inout] iterator Iterator obtained from nvs_entry_find or nvs_entry_find_in_handle -* function. Must be non-NULL. If any error except ESP_ERR_INVALID_ARG -* occurs, \c iterator is set to NULL. If ESP_ERR_INVALID_ARG occurs, \c -* iterator is not changed. -* -* @return -* - ESP_OK if no internal error or programming error occurred. -* - ESP_ERR_NVS_NOT_FOUND if no next element matching the iterator criteria. -* - ESP_ERR_INVALID_ARG if \c iterator is NULL. -* - Possibly other errors in the future for internal programming or flash errors. -*/ -esp_err_t nvs_entry_next(nvs_iterator_t *iterator); - -/** -* @brief Fills nvs_entry_info_t structure with information about entry pointed to by the iterator. -* -* @param[in] iterator Iterator obtained from nvs_entry_find or nvs_entry_find_in_handle -* function. Must be non-NULL. -* -* @param[out] out_info Structure to which entry information is copied. -* -* @return -* - ESP_OK if all parameters are valid; current iterator data has been written to out_info -* - ESP_ERR_INVALID_ARG if one of the parameters is NULL. -*/ -esp_err_t nvs_entry_info(const nvs_iterator_t iterator, nvs_entry_info_t *out_info); - -/** -* @brief Release iterator -* -* @param[in] iterator Release iterator obtained from nvs_entry_find or -* nvs_entry_find_in_handle or nvs_entry_next function. NULL argument is -* allowed. -* -*/ -void nvs_release_iterator(nvs_iterator_t iterator); - - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif //ESP_NVS_H diff --git a/test/support/protobuf-c/CMakeLists.txt b/test/support/protobuf-c/CMakeLists.txt deleted file mode 100644 index 7b6d49d0..00000000 --- a/test/support/protobuf-c/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_library(idf_protobuf-c STATIC) -add_library(idf::protobuf-c ALIAS idf_protobuf-c) - -target_sources(idf_protobuf-c PRIVATE protobuf-c/protobuf-c/protobuf-c.c) -target_include_directories(idf_protobuf-c PUBLIC protobuf-c) diff --git a/test/support/protobuf-c/protobuf-c b/test/support/protobuf-c/protobuf-c deleted file mode 160000 index abc67a11..00000000 --- a/test/support/protobuf-c/protobuf-c +++ /dev/null @@ -1 +0,0 @@ -Subproject commit abc67a11c6db271bedbb9f58be85d6f4e2ea8389 diff --git a/thirdparty/uWebSockets/CMakeLists.txt b/thirdparty/uWebSockets/CMakeLists.txt new file mode 100644 index 00000000..a7650958 --- /dev/null +++ b/thirdparty/uWebSockets/CMakeLists.txt @@ -0,0 +1,33 @@ +add_library(shrapnel_uwebsockets STATIC) +add_library(shrapnel::uwebsockets ALIAS shrapnel_uwebsockets) + +target_include_directories(shrapnel_uwebsockets + PUBLIC + uWebSockets/src + uWebSockets/uSockets/src +) + +target_compile_definitions(shrapnel_uwebsockets + PUBLIC + LIBUS_NO_SSL + UWS_NO_ZLIB +) + +target_sources(shrapnel_uwebsockets + PRIVATE + ./uWebSockets/uSockets/src/bsd.c + ./uWebSockets/uSockets/src/context.c + ./uWebSockets/uSockets/src/crypto/openssl.c + ./uWebSockets/uSockets/src/crypto/sni_tree.cpp + ./uWebSockets/uSockets/src/eventing/asio.cpp + ./uWebSockets/uSockets/src/eventing/epoll_kqueue.c + ./uWebSockets/uSockets/src/eventing/gcd.c + ./uWebSockets/uSockets/src/eventing/libuv.c + ./uWebSockets/uSockets/src/io_uring/io_context.c + ./uWebSockets/uSockets/src/io_uring/io_loop.c + ./uWebSockets/uSockets/src/io_uring/io_socket.c + ./uWebSockets/uSockets/src/loop.c + ./uWebSockets/uSockets/src/quic.c + ./uWebSockets/uSockets/src/socket.c + ./uWebSockets/uSockets/src/udp.c +) diff --git a/thirdparty/uWebSockets/uWebSockets b/thirdparty/uWebSockets/uWebSockets new file mode 160000 index 00000000..d044f276 --- /dev/null +++ b/thirdparty/uWebSockets/uWebSockets @@ -0,0 +1 @@ +Subproject commit d044f276b60edd497db8d7724d9c042924813a27