diff --git a/examples/cxx/experimental/esp_event_async_cxx/CMakeLists.txt b/examples/cxx/experimental/esp_event_async_cxx/CMakeLists.txt new file mode 100644 index 0000000000..c5f5da3014 --- /dev/null +++ b/examples/cxx/experimental/esp_event_async_cxx/CMakeLists.txt @@ -0,0 +1,10 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +# (Not part of the boilerplate) +# This example uses an experimental c++ component. +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(esp_event_async_cxx) diff --git a/examples/cxx/experimental/esp_event_async_cxx/Makefile b/examples/cxx/experimental/esp_event_async_cxx/Makefile new file mode 100644 index 0000000000..2ffc7368bb --- /dev/null +++ b/examples/cxx/experimental/esp_event_async_cxx/Makefile @@ -0,0 +1,11 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := esp_event_async_cxx + +EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/cxx/experimental/experimental_cpp_component + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/cxx/experimental/esp_event_async_cxx/README.md b/examples/cxx/experimental/esp_event_async_cxx/README.md new file mode 100644 index 0000000000..bc52645dda --- /dev/null +++ b/examples/cxx/experimental/esp_event_async_cxx/README.md @@ -0,0 +1,41 @@ +# ESP-Event asynchronous example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + + +## How to use example + +### Configure the project + +``` +idf.py menuconfig +``` + +* Set serial port under Serial Flasher Options. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +The object is created twice, hence the started Eventloop and finished destruction lines appear twice. +``` +NORMAL TESTING... +received event: test/0; data: 47 +received event: test/1 + +TIMEOUT TESTING... +received event: test/0 + +TIMEOUT for event: test/0 +I (10419) ESP Event C++ Async: Finished example +``` diff --git a/examples/cxx/experimental/esp_event_async_cxx/main/CMakeLists.txt b/examples/cxx/experimental/esp_event_async_cxx/main/CMakeLists.txt new file mode 100644 index 0000000000..3eb45d4792 --- /dev/null +++ b/examples/cxx/experimental/esp_event_async_cxx/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "esp_event_async_cxx_example.cpp" + INCLUDE_DIRS ".") diff --git a/examples/cxx/experimental/esp_event_async_cxx/main/component.mk b/examples/cxx/experimental/esp_event_async_cxx/main/component.mk new file mode 100644 index 0000000000..61f8990c31 --- /dev/null +++ b/examples/cxx/experimental/esp_event_async_cxx/main/component.mk @@ -0,0 +1,8 @@ +# +# Main component makefile. +# +# This Makefile can be left empty. By default, it will take the sources in the +# src/ directory, compile them and link them into lib(subdirectory_name).a +# in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# diff --git a/examples/cxx/experimental/esp_event_async_cxx/main/esp_event_async_cxx_example.cpp b/examples/cxx/experimental/esp_event_async_cxx/main/esp_event_async_cxx_example.cpp new file mode 100644 index 0000000000..0575cc9547 --- /dev/null +++ b/examples/cxx/experimental/esp_event_async_cxx/main/esp_event_async_cxx_example.cpp @@ -0,0 +1,110 @@ +/* ESP Event C++ Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "esp_event_cxx.hpp" +#include "esp_event.h" +#include "esp_err.h" + +using namespace idf::event; +using namespace std; + +ESP_EVENT_DEFINE_BASE(TEST_EVENT_BASE); +const ESPEventID TEST_EVENT_ID_0(0); +const ESPEventID TEST_EVENT_ID_1(1); + +ESPEvent TEMPLATE_EVENT_0(TEST_EVENT_BASE, TEST_EVENT_ID_0); +ESPEvent TEMPLATE_EVENT_1(TEST_EVENT_BASE, TEST_EVENT_ID_1); + +// We use "normal" static functions here. However, passing std::function types also works with +// ESPEventLoop::register_event() and ESPEventLoop::register_event_timed(), allowing to reference custom data. +static void callback(const ESPEvent &event, void *data) +{ + cout << "received event: " << event.base << "/" << event.id; + if (data) { + cout << "; data: " << *(static_cast(data)); + } + cout << endl; +}; + +static void timeout_callback(const ESPEvent &event) +{ + cout << "TIMEOUT for event: " << event.base << "/" << event.id << endl; +}; + +extern "C" void app_main(void) +{ + { + cout << "Normal testing..." << endl; + ESPEventLoop loop; + int data = 47; + int captured_data = 42; + + unique_ptr reg_1 = loop.register_event(TEMPLATE_EVENT_0, + [captured_data](const ESPEvent &event, void *data) { + cout << "received event: " << event.base << "/" << event.id; + if (data) { + cout << "; event data: " << *(static_cast(data)); + } + cout << "; handler data: " << captured_data << endl; + }); + unique_ptr reg_2; + + // Run for 4 seconds... + for (int i = 0; i < 4; i++) { + switch (i) { + case 0: + // will be received + loop.post_event_data(TEMPLATE_EVENT_0, data); + break; + case 1: + // will NOT be received because TEST_EVENT_ID_1 hasn't been registered yet + loop.post_event_data(TEMPLATE_EVENT_1); + break; + case 2: + // register TEST_EVENT_ID_1 + reg_2 = loop.register_event(TEMPLATE_EVENT_1, callback); + // will be received + loop.post_event_data(TEMPLATE_EVENT_1); + break; + case 3: + // unregister callback with TEST_EVENT_ID_1 again + reg_2.reset(); + // will NOT be received + loop.post_event_data(TEMPLATE_EVENT_1); + break; + + } + this_thread::sleep_for(chrono::seconds(1)); + } + } + + { + cout << endl << "Timeout testing..." << endl; + ESPEventLoop loop; + + // Setting timeout and sending event early enough. + unique_ptr timed_reg = loop.register_event_timed(TEMPLATE_EVENT_0, + callback, + chrono::milliseconds(500), + timeout_callback); + loop.post_event_data(TEMPLATE_EVENT_0); + cout << endl; + + // Setting timeout and sending event too late. + // Note: the old registration will be properly unregistered by resetting the unique_ptr. + timed_reg = loop.register_event_timed(TEMPLATE_EVENT_0, + callback, + chrono::milliseconds(500), + timeout_callback); + this_thread::sleep_for(chrono::seconds(1)); + loop.post_event_data(TEMPLATE_EVENT_0); + + } + cout << "Finished example" << endl; +} diff --git a/examples/cxx/experimental/esp_event_async_cxx/sdkconfig.defaults b/examples/cxx/experimental/esp_event_async_cxx/sdkconfig.defaults new file mode 100644 index 0000000000..a365ac6589 --- /dev/null +++ b/examples/cxx/experimental/esp_event_async_cxx/sdkconfig.defaults @@ -0,0 +1,3 @@ +# Enable C++ exceptions and set emergency pool size for exception objects +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=1024 diff --git a/examples/cxx/experimental/esp_event_cxx/CMakeLists.txt b/examples/cxx/experimental/esp_event_cxx/CMakeLists.txt new file mode 100644 index 0000000000..1f8c05edcb --- /dev/null +++ b/examples/cxx/experimental/esp_event_cxx/CMakeLists.txt @@ -0,0 +1,10 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +# (Not part of the boilerplate) +# This example uses an experimental c++ component. +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(esp_event_cxx) diff --git a/examples/cxx/experimental/esp_event_cxx/Makefile b/examples/cxx/experimental/esp_event_cxx/Makefile new file mode 100644 index 0000000000..ece170bf6c --- /dev/null +++ b/examples/cxx/experimental/esp_event_cxx/Makefile @@ -0,0 +1,11 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := esp_event_cxx + +EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/cxx/experimental/experimental_cpp_component + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/cxx/experimental/esp_event_cxx/README.md b/examples/cxx/experimental/esp_event_cxx/README.md new file mode 100644 index 0000000000..6ad7a221dc --- /dev/null +++ b/examples/cxx/experimental/esp_event_cxx/README.md @@ -0,0 +1,44 @@ +# ESP Event synchronous example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + + +## How to use example + +### Configure the project + +``` +idf.py menuconfig +``` + +* Set serial port under Serial Flasher Options. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +``` +I (409) ESP Event C++: started event loop +event base test, ID: 0; called first round +event base test, ID: 1; called first round +received timeout +event base test, ID: 0; called second round +event base test, ID: 1; called second round +event base test, ID: 0; called second round +event base test, ID: 1; called second round +event base test, ID: 0; called second round +event base test, ID: 1; called second round +event base test, ID: 0; called second round +event base test, ID: 1; called second round +I (10419) ESP Event C++: Missed: 0 events +``` diff --git a/examples/cxx/experimental/esp_event_cxx/main/CMakeLists.txt b/examples/cxx/experimental/esp_event_cxx/main/CMakeLists.txt new file mode 100644 index 0000000000..8170a3920d --- /dev/null +++ b/examples/cxx/experimental/esp_event_cxx/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "esp_event_cxx_example.cpp" + INCLUDE_DIRS ".") diff --git a/examples/cxx/experimental/esp_event_cxx/main/component.mk b/examples/cxx/experimental/esp_event_cxx/main/component.mk new file mode 100644 index 0000000000..61f8990c31 --- /dev/null +++ b/examples/cxx/experimental/esp_event_cxx/main/component.mk @@ -0,0 +1,8 @@ +# +# Main component makefile. +# +# This Makefile can be left empty. By default, it will take the sources in the +# src/ directory, compile them and link them into lib(subdirectory_name).a +# in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# diff --git a/examples/cxx/experimental/esp_event_cxx/main/esp_event_cxx_example.cpp b/examples/cxx/experimental/esp_event_cxx/main/esp_event_cxx_example.cpp new file mode 100644 index 0000000000..7c9d974597 --- /dev/null +++ b/examples/cxx/experimental/esp_event_cxx/main/esp_event_cxx_example.cpp @@ -0,0 +1,80 @@ +/* ESP Event C++ Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include +#include "esp_event_cxx.hpp" +#include "esp_event.h" +#include "esp_err.h" + +using namespace idf::event; +using namespace std; + +#define EVENT_NUM 10 + +ESP_EVENT_DEFINE_BASE(TEST_EVENT_BASE); +static ESPEventID TEST_EVENT_ID_0(0); +static ESPEventID TEST_EVENT_ID_1(1); + +// the events we want to register +static ESPEvent TEMPLATE_EVENT_0(TEST_EVENT_BASE, TEST_EVENT_ID_0); +static ESPEvent TEMPLATE_EVENT_1(TEST_EVENT_BASE, TEST_EVENT_ID_1); + +// helper function to post events, simulating an event source +void post_events() { + for (int i = 0; i < EVENT_NUM; i++) { + ESP_ERROR_CHECK(esp_event_post(TEST_EVENT_BASE, + i % 2 ? TEST_EVENT_ID_1.get_id() : TEST_EVENT_ID_0.get_id(), + nullptr, + 0, + portMAX_DELAY)); + this_thread::sleep_for(chrono::seconds(1)); + } +} + +extern "C" void app_main(void) +{ + ESPEventHandlerSync event_handler(make_shared()); + event_handler.listen_to(TEMPLATE_EVENT_0); + event_handler.listen_to(TEMPLATE_EVENT_1); + cout << "started event loop" << endl; + + thread th(post_events); + + // waiting for two events to be posted via post_events() + this_thread::sleep_for(chrono::milliseconds(1100)); + + // reading the two already received events, then running into timeout + for (;;) { + ESPEventHandlerSync::EventResultTimed result = event_handler.wait_event_for(std::chrono::milliseconds(500)); + + if (result.timeout) { // if timeout, then the default esp event will be sent with invalid base + break; + } else { + cout << "event base " << result.event.base + << ", ID: " << result.event.id + << "; called first round" << endl; + } + } + + cout << "received timeout" << endl; + this_thread::sleep_for(chrono::milliseconds(2000)); + + // Read the events we missed up until now and then continue reading + for (int i = 0; i < EVENT_NUM - 2; i++) { + ESPEventHandlerSync::EventResult result = event_handler.wait_event(); + cout << "event base " << result.event.base + << ", ID: " << result.event.id + << "; called second round" << endl; + } + + th.join(); + + // checking whether events were missed by the ESPEventHandlerSync class + cout << "Missed: " << event_handler.get_send_queue_errors() << " events" << endl; +} diff --git a/examples/cxx/experimental/esp_event_cxx/sdkconfig.defaults b/examples/cxx/experimental/esp_event_cxx/sdkconfig.defaults new file mode 100644 index 0000000000..a365ac6589 --- /dev/null +++ b/examples/cxx/experimental/esp_event_cxx/sdkconfig.defaults @@ -0,0 +1,3 @@ +# Enable C++ exceptions and set emergency pool size for exception objects +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=1024 diff --git a/examples/cxx/experimental/experimental_cpp_component/CMakeLists.txt b/examples/cxx/experimental/experimental_cpp_component/CMakeLists.txt index 858e1c114b..434ac51173 100644 --- a/examples/cxx/experimental/experimental_cpp_component/CMakeLists.txt +++ b/examples/cxx/experimental/experimental_cpp_component/CMakeLists.txt @@ -1,3 +1,3 @@ -idf_component_register(SRCS "esp_exception.cpp" "i2c_cxx.cpp" +idf_component_register(SRCS "esp_exception.cpp" "i2c_cxx.cpp" "esp_event_api.cpp" "esp_event_cxx.cpp" INCLUDE_DIRS "include" - REQUIRES driver) + REQUIRES driver esp_event) diff --git a/examples/cxx/experimental/experimental_cpp_component/esp_event_api.cpp b/examples/cxx/experimental/experimental_cpp_component/esp_event_api.cpp new file mode 100644 index 0000000000..bc17945587 --- /dev/null +++ b/examples/cxx/experimental/experimental_cpp_component/esp_event_api.cpp @@ -0,0 +1,116 @@ +#include "esp_event.h" +#include "esp_event_cxx.hpp" +#include "esp_event_api.hpp" + +#ifdef __cpp_exceptions + +namespace idf { + +namespace event { + +ESPEventAPIDefault::ESPEventAPIDefault() +{ + esp_err_t res = esp_event_loop_create_default(); + if (res != ESP_OK) { + throw idf::event::EventException(res); + } +} + +ESPEventAPIDefault::~ESPEventAPIDefault() +{ + esp_event_loop_delete_default(); +} + +esp_err_t ESPEventAPIDefault::handler_register(esp_event_base_t event_base, + int32_t event_id, + esp_event_handler_t event_handler, + void *event_handler_arg, + esp_event_handler_instance_t *instance) +{ + return esp_event_handler_instance_register(event_base, + event_id, + event_handler, + event_handler_arg, + instance); +} + +esp_err_t ESPEventAPIDefault::handler_unregister(esp_event_base_t event_base, + int32_t event_id, + esp_event_handler_instance_t instance) +{ + return esp_event_handler_instance_unregister(event_base, event_id, instance); +} + +esp_err_t ESPEventAPIDefault::post(esp_event_base_t event_base, + int32_t event_id, + void* event_data, + size_t event_data_size, + TickType_t ticks_to_wait) +{ + return esp_event_post(event_base, + event_id, + event_data, + event_data_size, + ticks_to_wait); + +} + +ESPEventAPICustom::ESPEventAPICustom(const esp_event_loop_args_t &event_loop_args) +{ + esp_err_t res = esp_event_loop_create(&event_loop_args, &event_loop); + if (res != ESP_OK) { + throw idf::event::EventException(res); + } +} + +ESPEventAPICustom::~ESPEventAPICustom() +{ + esp_event_loop_delete(event_loop); +} + +esp_err_t ESPEventAPICustom::handler_register(esp_event_base_t event_base, + int32_t event_id, + esp_event_handler_t event_handler, + void *event_handler_arg, + esp_event_handler_instance_t *instance) +{ + return esp_event_handler_instance_register_with(event_loop, + event_base, + event_id, + event_handler, + event_handler_arg, + instance); +} + +esp_err_t ESPEventAPICustom::handler_unregister(esp_event_base_t event_base, + int32_t event_id, + esp_event_handler_instance_t instance) +{ + return esp_event_handler_instance_unregister_with(event_loop, event_base, event_id, instance); +} + +esp_err_t ESPEventAPICustom::post(esp_event_base_t event_base, + int32_t event_id, + void* event_data, + size_t event_data_size, + TickType_t ticks_to_wait) +{ + return esp_event_post_to(event_loop, + event_base, + event_id, + event_data, + event_data_size, + ticks_to_wait); +} + +esp_err_t ESPEventAPICustom::run(TickType_t ticks_to_run) +{ + return esp_event_loop_run(event_loop, ticks_to_run); +} + +} // event + +} // idf + +#endif // __cpp_exceptions + diff --git a/examples/cxx/experimental/experimental_cpp_component/esp_event_cxx.cpp b/examples/cxx/experimental/experimental_cpp_component/esp_event_cxx.cpp new file mode 100644 index 0000000000..83f40949bc --- /dev/null +++ b/examples/cxx/experimental/experimental_cpp_component/esp_event_cxx.cpp @@ -0,0 +1,225 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp_event_cxx.hpp" + +#ifdef __cpp_exceptions + +using namespace idf::event; +using namespace std; + +namespace idf { + +namespace event { + +const std::chrono::milliseconds PLATFORM_MAX_DELAY_MS(portMAX_DELAY *portTICK_PERIOD_MS); + +ESPEventReg::ESPEventReg(std::function cb, + const ESPEvent& ev, + std::shared_ptr api) + : cb(cb), event(ev), api(api) +{ + if (!cb) throw EventException(ESP_ERR_INVALID_ARG); + if (!api) throw EventException(ESP_ERR_INVALID_ARG); + + esp_err_t reg_result = api->handler_register(ev.base, ev.id.get_id(), event_handler_hook, this, &instance); + if (reg_result != ESP_OK) { + throw ESPEventRegisterException(reg_result, event); + } +} + +ESPEventReg::~ESPEventReg() +{ + api->handler_unregister(event.base, event.id.get_id(), instance); +} + +void ESPEventReg::dispatch_event_handling(ESPEvent event, void *event_data) +{ + cb(event, event_data); +} + +void ESPEventReg::event_handler_hook(void *handler_arg, + esp_event_base_t event_base, + int32_t event_id, + void *event_data) +{ + ESPEventReg *object = static_cast(handler_arg); + object->dispatch_event_handling(ESPEvent(event_base, ESPEventID(event_id)), event_data); +} + +ESPEventRegTimed::ESPEventRegTimed(std::function cb, + const ESPEvent& ev, + std::function timeout_cb, + const std::chrono::microseconds &timeout, + std::shared_ptr api) + : ESPEventReg(cb, ev, api), timeout_cb(timeout_cb) +{ + if (!timeout_cb || timeout < MIN_TIMEOUT) { + throw EventException(ESP_ERR_INVALID_ARG); + } + + const esp_timer_create_args_t oneshot_timer_args { + timer_cb_hook, + static_cast(this), + ESP_TIMER_TASK, + "event" + }; + + esp_err_t res = esp_timer_create(&oneshot_timer_args, &timer); + if (res != ESP_OK) { + throw EventException(res); + } + + esp_err_t timer_result = esp_timer_start_once(timer, timeout.count()); + if (timer_result != ESP_OK) { + esp_timer_delete(timer); + throw EventException(timer_result); + } +} + +ESPEventRegTimed::~ESPEventRegTimed() +{ + std::lock_guard guard(timeout_mutex); + esp_timer_stop(timer); + esp_timer_delete(timer); + // TODO: is it guaranteed that there is no pending timer callback for timer? +} + +void ESPEventRegTimed::dispatch_event_handling(ESPEvent event, void *event_data) +{ + if (timeout_mutex.try_lock()) { + esp_timer_stop(timer); + cb(event, event_data); + timeout_mutex.unlock(); + } +} + +void ESPEventRegTimed::timer_cb_hook(void *arg) +{ + ESPEventRegTimed *object = static_cast(arg); + if (object->timeout_mutex.try_lock()) { + object->timeout_cb(object->event); + object->api->handler_unregister(object->event.base, object->event.id.get_id(), object->instance); + object->timeout_mutex.unlock(); + } +} + +ESPEventLoop::ESPEventLoop(std::shared_ptr api) : api(api) { + if (!api) throw EventException(ESP_ERR_INVALID_ARG); +} + +ESPEventLoop::~ESPEventLoop() { } + +unique_ptr ESPEventLoop::register_event(const ESPEvent &event, + function cb) +{ + return unique_ptr(new ESPEventReg(cb, event, api)); +} + +std::unique_ptr ESPEventLoop::register_event_timed(const ESPEvent &event, + std::function cb, + const std::chrono::microseconds &timeout, + std::function timer_cb) +{ + return std::unique_ptr(new ESPEventRegTimed(cb, event, timer_cb, timeout, api)); +} + +void ESPEventLoop::post_event_data(const ESPEvent &event, + const chrono::milliseconds &wait_time) +{ + esp_err_t result = api->post(event.base, + event.id.get_id(), + nullptr, + 0, + convert_ms_to_ticks(wait_time)); + + if (result != ESP_OK) { + throw ESPException(result); + } +} + +ESPEventHandlerSync::ESPEventHandlerSync(std::shared_ptr event_loop, + size_t queue_max_size, + TickType_t queue_send_timeout) + : send_queue_errors(0), + queue_send_timeout(queue_send_timeout), + event_loop(event_loop) +{ + if (!event_loop) throw EventException(ESP_ERR_INVALID_ARG); + if (queue_max_size < 1) throw EventException(ESP_ERR_INVALID_ARG); + + event_queue = xQueueCreate(queue_max_size, sizeof(EventResult)); + if (event_queue == nullptr) { + esp_event_loop_delete_default(); + throw EventException(ESP_FAIL); + } +} + +ESPEventHandlerSync::~ESPEventHandlerSync() +{ + vQueueDelete(event_queue); +} + +ESPEventHandlerSync::EventResult ESPEventHandlerSync::wait_event() +{ + EventResult event_result; + BaseType_t result = pdFALSE; + while (result != pdTRUE) { + result = xQueueReceive(event_queue, &event_result, convert_ms_to_ticks(PLATFORM_MAX_DELAY_MS)); + } + + return event_result; +} + +ESPEventHandlerSync::EventResultTimed ESPEventHandlerSync::wait_event_for(const std::chrono::milliseconds &timeout) +{ + EventResult event_result; + BaseType_t result = xQueueReceive(event_queue, &event_result, convert_ms_to_ticks(timeout)); + + EventResultTimed event_result_timed(event_result, result != pdTRUE); + return event_result_timed; +} + +void ESPEventHandlerSync::listen_to(const ESPEvent &event) +{ + std::shared_ptr reg = event_loop->register_event(event, [this](const ESPEvent &event, void *data) { + EventResult result(event, data); + post_event(result); + }); + registry.push_back(reg); +} + +void ESPEventHandlerSync::post_event(const EventResult &event_result) +{ + BaseType_t result = xQueueSendToBack(event_queue, (void *) &event_result, queue_send_timeout); + if (result != pdTRUE) { + ++send_queue_errors; + } +} + +size_t ESPEventHandlerSync::get_send_queue_errors() const +{ + return send_queue_errors; +} + +TickType_t convert_ms_to_ticks(const std::chrono::milliseconds &time) +{ + return time.count() / portTICK_PERIOD_MS; +} + +} // namespace event + +} // namespace idf + +#endif // __cpp_exceptions diff --git a/examples/cxx/experimental/experimental_cpp_component/esp_exception.cpp b/examples/cxx/experimental/experimental_cpp_component/esp_exception.cpp index dce4b0ed6c..3ac2661cd0 100644 --- a/examples/cxx/experimental/experimental_cpp_component/esp_exception.cpp +++ b/examples/cxx/experimental/experimental_cpp_component/esp_exception.cpp @@ -20,6 +20,10 @@ namespace idf { ESPException::ESPException(esp_err_t error) : error(error) { } +const char *ESPException::what() const noexcept { + return esp_err_to_name(error); +} + } // namespace idf #endif // __cpp_exceptions diff --git a/examples/cxx/experimental/experimental_cpp_component/include/esp_event_api.hpp b/examples/cxx/experimental/experimental_cpp_component/include/esp_event_api.hpp new file mode 100644 index 0000000000..f14b2256e0 --- /dev/null +++ b/examples/cxx/experimental/experimental_cpp_component/include/esp_event_api.hpp @@ -0,0 +1,123 @@ +#ifndef ESP_EVENT_API_HPP_ +#define ESP_EVENT_API_HPP_ + +#include "esp_event.h" + +namespace idf { + +namespace event { + +/** + * Abstract interface for direct calls to esp_event C-API. + * This is generally not intended to be used directly. + * It's main purpose is to provide ESPEventLoop a unified API not dependent on whether the default event loop or a + * custom event loop is used. + * The interface resembles the C-API, have a look there for further documentation. + */ +class ESPEventAPI { +public: + virtual ~ESPEventAPI() { } + + virtual esp_err_t handler_register(esp_event_base_t event_base, + int32_t event_id, + esp_event_handler_t event_handler, + void* event_handler_arg, + esp_event_handler_instance_t *instance) = 0; + + virtual esp_err_t handler_unregister(esp_event_base_t event_base, + int32_t event_id, + esp_event_handler_instance_t instance) = 0; + + virtual esp_err_t post(esp_event_base_t event_base, + int32_t event_id, + void* event_data, + size_t event_data_size, + TickType_t ticks_to_wait) = 0; +}; + +/** + * @brief API version with default event loop. + * + * It will direct calls to the default event loop API. + */ +class ESPEventAPIDefault : public ESPEventAPI { +public: + ESPEventAPIDefault(); + virtual ~ESPEventAPIDefault(); + + /** + * Copying would lead to deletion of event loop through destructor. + */ + ESPEventAPIDefault(const ESPEventAPIDefault &o) = delete; + ESPEventAPIDefault& operator=(const ESPEventAPIDefault&) = delete; + + esp_err_t handler_register(esp_event_base_t event_base, + int32_t event_id, + esp_event_handler_t event_handler, + void* event_handler_arg, + esp_event_handler_instance_t *instance) override; + + esp_err_t handler_unregister(esp_event_base_t event_base, + int32_t event_id, + esp_event_handler_instance_t instance) override; + + esp_err_t post(esp_event_base_t event_base, + int32_t event_id, + void* event_data, + size_t event_data_size, + TickType_t ticks_to_wait) override; +}; + +/** + * @brief API version with custom event loop. + * + * It will direct calls to the custom event loop API. + * The loop parameters are given in the constructor the same way it's done in esp_event_loop_create() in event.h. + * This class also provides a run method in case the custom event loop was created without its own task. + */ +class ESPEventAPICustom : public ESPEventAPI { +public: + /** + * @param event_loop_args the event loop arguments, refer to esp_event_loop_create() in event.h. + */ + ESPEventAPICustom(const esp_event_loop_args_t &event_loop_args); + + virtual ~ESPEventAPICustom(); + + /** + * Copying would lead to deletion of event loop through destructor. + */ + ESPEventAPICustom(const ESPEventAPICustom &o) = delete; + ESPEventAPICustom& operator=(const ESPEventAPICustom&) = delete; + + esp_err_t handler_register(esp_event_base_t event_base, + int32_t event_id, + esp_event_handler_t event_handler, + void* event_handler_arg, + esp_event_handler_instance_t *instance) override; + + esp_err_t handler_unregister(esp_event_base_t event_base, + int32_t event_id, + esp_event_handler_instance_t instance) override; + + esp_err_t post(esp_event_base_t event_base, + int32_t event_id, + void* event_data, + size_t event_data_size, + TickType_t ticks_to_wait) override; + + /** + * Run the event loop. The behavior is the same as esp_event_loop_run in esp_event.h. + */ + esp_err_t run(TickType_t ticks_to_run); + +private: + esp_event_loop_handle_t event_loop; +}; + +} // event + +} // idf + +#endif // ESP_EVENT_API_HPP_ + diff --git a/examples/cxx/experimental/experimental_cpp_component/include/esp_event_cxx.hpp b/examples/cxx/experimental/experimental_cpp_component/include/esp_event_cxx.hpp new file mode 100644 index 0000000000..3efd3bde44 --- /dev/null +++ b/examples/cxx/experimental/experimental_cpp_component/include/esp_event_cxx.hpp @@ -0,0 +1,470 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ESP_EVENT_CXX_H_ +#define ESP_EVENT_CXX_H_ + +#ifdef __cpp_exceptions + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "esp_timer.h" +#include "esp_err.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" + +#include "esp_exception.hpp" +#include "esp_event_api.hpp" + +namespace idf { + +namespace event { + +extern const std::chrono::milliseconds PLATFORM_MAX_DELAY_MS; + +const std::chrono::microseconds MIN_TIMEOUT(200); + +class EventException : public ESPException { +public: + EventException(esp_err_t error) : ESPException(error) { } +}; + +/** + * @brief + * Thrown to signal a timeout in EventHandlerSync. + */ +class EventTimeout : public idf::event::EventException { +public: + EventTimeout(esp_err_t error) : EventException(error) { } +}; + +/** + * @brief + * Event ID wrapper class to make C++ APIs more explicit. + * + * This prevents APIs from taking raw ints as event IDs which are not very expressive and may be + * confused with other parameters of a function. + */ +class ESPEventID { +public: + ESPEventID() : id(0) { } + explicit ESPEventID(int32_t event_id) : id(event_id) { } + + inline bool operator==(const ESPEventID &rhs) const { + return id == rhs.get_id(); + } + + inline ESPEventID &operator=(const ESPEventID& other) { + id = other.id; + return *this; + } + + inline int32_t get_id() const { + return id; + } + + friend std::ostream& operator<<(std::ostream& os, const ESPEventID& id); + +private: + int32_t id; +}; + +inline std::ostream& operator<<(std::ostream &os, const ESPEventID& id) { + os << id.id; + return os; +} + +/* + * Helper struct to bundle event base and event ID. + */ +struct ESPEvent { + ESPEvent() + : base(nullptr), id() { } + ESPEvent(esp_event_base_t event_base, const ESPEventID &event_id) + : base(event_base), id(event_id) { } + + esp_event_base_t base; + ESPEventID id; +}; + +/** + * Thrown if event registration, i.e. \c register_event() or \c register_event_timed(), fails. + */ +struct ESPEventRegisterException : public EventException { + ESPEventRegisterException(esp_err_t err, const ESPEvent& event) + : EventException(err), esp_event(event) { } + + const char *what() const noexcept + { + std::string ret_message = "Event base: " + std::string(esp_event.base) + + ", Event ID: " + std::to_string(esp_event.id.get_id()); + return ret_message.c_str(); + } + + const ESPEvent esp_event; +}; + +inline bool operator==(const ESPEvent &lhs, const ESPEvent &rhs) +{ + return lhs.base == rhs.base && lhs.id == rhs.id; +} + +TickType_t convert_ms_to_ticks(const std::chrono::milliseconds &time); + +/** + * Callback-event combination for ESPEventLoop. + * + * Used to bind class-based handler instances to event_handler_hook which is registered into the C-based + * esp event loop. + * It can be used directly, however, the recommended way is to obtain a unique_ptr via ESPEventLoop::register_event(). + */ +class ESPEventReg { +public: + /** + * Register the event handler \c cb to handle the events defined by \c ev. + * + * @param cb The handler to be called. + * @param ev The event for which the handler is registered. + * @param api The esp event api implementation. + */ + ESPEventReg(std::function cb, + const ESPEvent& ev, + std::shared_ptr api); + + /** + * Unregister the event handler. + */ + virtual ~ESPEventReg(); + +protected: + /** + * This is esp_event's handler, all events registered go through this. + */ + static void event_handler_hook(void *handler_arg, + esp_event_base_t event_base, + int32_t event_id, + void *event_data); + + /** + * User event handler. + */ + std::function cb; + + /** + * Helper function to enter the instance's scope from the generic \c event_handler_hook(). + */ + virtual void dispatch_event_handling(ESPEvent event, void *event_data); + + /** + * Save the event here to be able to un-register from the event loop on destruction. + */ + ESPEvent event; + + /** + * This API handle allows different sets of APIs to be applied, e.g. default event loop API and + * custom event loop API. + */ + std::shared_ptr api; + + /** + * Event handler instance from the esp event C API. + */ + esp_event_handler_instance_t instance; +}; + +/** + * Callback-event combination for ESPEventLoop with builtin timeout. + * + * Used to bind class-based handler instances to event_handler_hook which is registered into the C-based + * esp event loop. + * It can be used directly, however, the recommended way is to obtain a unique_ptr via ESPEventLoop::register_event(). + */ +class ESPEventRegTimed : public ESPEventReg { +public: + /** + * Register the event handler \c cb to handle the events as well as a timeout callback in case the event doesn't + * arrive on time. + * + * If the event \c ev is received before \c timeout milliseconds, then the event handler is invoked. + * If no such event is received before \c timeout milliseconds, then the timeout callback is invoked. + * After the timeout or the first occurance of the event, the timer will be deactivated. + * The event handler registration will only be deactivated if the timeout occurs. + * If event handler and timeout occur at the same time, only either the event handler or the timeout callback + * will be invoked. + * + * @param cb The handler to be called. + * @param ev The event for which the handler is registered. + * @param timeout_cb The timeout callback which is called in case there is no event for \c timeout microseconds. + * @param timeout The timeout in microseconds. + * @param api The esp event api implementation. + */ + ESPEventRegTimed(std::function cb, + const ESPEvent& ev, + std::function timeout_cb, + const std::chrono::microseconds &timeout, + std::shared_ptr api); + + /** + * Unregister the event handler, stop and delete the timer. + */ + virtual ~ESPEventRegTimed(); + +protected: + + /** + * Helper function to hook directly into esp timer callback. + */ + static void timer_cb_hook(void *arg); + + /** + * Helper function to enter the instance's scope from the generic \c event_handler_hook(). + */ + void dispatch_event_handling(ESPEvent event, void *event_data) override; + + /** + * The timer callback which will be called on timeout. + */ + std::function timeout_cb; + + /** + * Timer used for event timeouts. + */ + esp_timer_handle_t timer; + + /** + * This mutex makes sure that a timeout and event callbacks aren't invoked both. + */ + std::mutex timeout_mutex; +}; + +class ESPEventLoop { +public: + /** + * Creates the ESP default event loop. + * + * @param api the interface to the esp_event api; this determines whether the default event loop is used + * or a custom loop (or just a mock up for tests). May be nullptr, in which case it will created + * here. + * + * @note may throw EventException + */ + ESPEventLoop(std::shared_ptr api = std::make_shared()); + + /** + * Deletes the event loop implementation (depends on \c api). + */ + virtual ~ESPEventLoop(); + + /** + * Registers a specific handler-event combination to the event loop. + * + * @return a reference to the combination of handler and event which can be used to unregister + * this combination again later on. + * + * @note registering the same event twice will result in unregistering the earlier registered handler. + * @note may throw EventException, ESPEventRegisterException + */ + std::unique_ptr register_event(const ESPEvent &event, + std::function cb); + + /** + * Sets a timeout for event. If the specified event isn't received within timeout, + * timer_cb is called. + * + * @note this is independent from the normal event handling. Hence, registering an event for + * timeout does not interfere with a different client that has registered normally for the + * same event. + */ + std::unique_ptr register_event_timed(const ESPEvent &event, + std::function cb, + const std::chrono::microseconds &timeout, + std::function timer_cb); + + /** + * Posts an event and corresponding data. + * + * @param event the event to post + * @param event_data The event data. A copy will be made internally and a pointer to the copy will be passed to the + * event handler. + * @param wait_time the maximum wait time the function tries to post the event + */ + template + void post_event_data(const ESPEvent &event, + T &event_data, + const std::chrono::milliseconds &wait_time = PLATFORM_MAX_DELAY_MS); + + /** + * Posts an event. + * + * No event data will be send. The event handler will receive a nullptr. + * + * @param event the event to post + * @param wait_time the maximum wait time the function tries to post the event + */ + void post_event_data(const ESPEvent &event, + const std::chrono::milliseconds &wait_time = PLATFORM_MAX_DELAY_MS); + +private: + /** + * This API handle allows different sets of APIs to be applied, e.g. default event loop API and + * custom event loop API. + */ + std::shared_ptr api; +}; + +/** + * ESPEventHandlerSync builds upon ESPEventLoop to create a class which allows synchronous event handling. + * + * It is built around a queue which buffers received events. This queue is also used to wait synchronously (blocking) + * for an event. The consequence is that once an event is registered with this class, it is guaranteed to be received + * as long as the queue can handle all incoming events (see \c get_send_queue_errors()). + */ +class ESPEventHandlerSync { +public: + /** + * Result type for synchronous waiting. + */ + struct EventResult { + EventResult() : event(), ev_data(nullptr) { } + EventResult(ESPEvent ev, void *ev_data) : event(ev), ev_data(ev_data) { } + ESPEvent event; + void *ev_data; + }; + + /** + * Result type for synchronous waiting with timeout. + */ + struct EventResultTimed : public EventResult { + EventResultTimed(EventResult event_result, bool timeout_arg) + : EventResult(event_result), timeout(timeout_arg) { } + bool timeout; + }; + + /** + * Sets up synchronous event handling and registers event with it. + * + * @param event_loop ESPEventLoop implementation to manage esp events. + * @param queue_max_size The queue size of the underlying FreeRTOS queue. + * The memory to store queue_max_size number of events is allocated during construction + * and held until destruction! + * @param queue_send_timeout The timeout for posting events to the internal queue + */ + ESPEventHandlerSync(std::shared_ptr event_loop, + size_t queue_max_size = 10, + TickType_t queue_send_timeout = 0); + + /** + * Unregister all formerly registered events via automatic destruction in registry. + */ + virtual ~ESPEventHandlerSync(); + + /** + * Waits for any of the events registered before with listen_to(). + */ + EventResult wait_event(); + + /** + * Waits for an event either PLATFORM_MAX_DELAY_MS ms or timeout ms. + * + * @param timeout the maximum waiting time for new events if no event is pending + * The timeout is restricted by the TickType_t and configTICK_RATE_HZ. + * TickType_t's width determines the maximum wait time. configTICK_RATE_HZ + * determines the minimum wait time. + * + * Throws EventTimeout in case of a timeout. + */ + EventResultTimed wait_event_for(const std::chrono::milliseconds &timeout); + + /** + * Register additional event to listen for. + * + * @note this will unregister all earlier registered events of the same event type from the event loop. + */ + void listen_to(const ESPEvent &event); + + /** + * Indicates whether there were errors inserting an event into the queue. + * This is the case e.g. if the queue with waiting events is full already. + * Use this function to adjust the queue size (\c queue_send_timeout in constructor) in your application. + */ + size_t get_send_queue_errors() const; + +protected: + /** + * Posts an event to the internal queue. + */ + void post_event(const EventResult &result); + +private: + /** + * Keeps track if there are any errors inserting an event into this class's event queue. + */ + std::atomic send_queue_errors; + + /** + * The queue which saves events if they were received already or waits if no event was + * received. + */ + QueueHandle_t event_queue; + + /** + * Timeout used to posting to the queue when using \c post_event(). Can be adjusted in constructor. + */ + TickType_t queue_send_timeout; + + /** + * The event loop used for this synchronous event handling class. + */ + std::shared_ptr event_loop; + + /** + * Keeps track of all events which are registered already for synchronous handling. + * + * This is necessary to keep the registration. + */ + std::vector > registry; +}; + +template +void ESPEventLoop::post_event_data(const ESPEvent &event, + T &event_data, + const std::chrono::milliseconds &wait_time) +{ + esp_err_t result = api->post(event.base, + event.id.get_id(), + &event_data, + sizeof(event_data), + convert_ms_to_ticks(wait_time)); + + if (result != ESP_OK) { + throw ESPException(result); + } +} + +} // namespace event + +} // namespace idf + +#endif // __cpp_exceptions + +#endif // ESP_EVENT_CXX_H_ diff --git a/examples/cxx/experimental/experimental_cpp_component/include/esp_exception.hpp b/examples/cxx/experimental/experimental_cpp_component/include/esp_exception.hpp index 1c59564889..feb3cc7e1c 100644 --- a/examples/cxx/experimental/experimental_cpp_component/include/esp_exception.hpp +++ b/examples/cxx/experimental/experimental_cpp_component/include/esp_exception.hpp @@ -22,13 +22,33 @@ namespace idf { /** - * General exception class for exceptions on the ESP chips. + * @brief + * General exception class for all C++ exceptions in IDF. + * * All throwing code in IDF should use either this exception directly or a sub-classes. + * An error from the underlying IDF function is mandatory. The idea is to wrap the orignal IDF error code to keep + * the error scheme partially compatible. If an exception occurs in a higher level C++ code not directly wrapping + * IDF functions, an appropriate error code reflecting the cause must be chosen or newly created. */ -struct ESPException : public std::exception { +class ESPException : public std::exception { +public: + /** + * @param error Error from underlying IDF functions. + */ ESPException(esp_err_t error); - esp_err_t error; + virtual ~ESPException() { } + + /** + * @return A textual representation of the contained error. This method only wraps \c esp_err_to_name. + */ + virtual const char *what() const noexcept; + + /** + * Error from underlying IDF functions. If an exception occurs in a higher level C++ code not directly wrapping + * IDF functions, an appropriate error code reflecting the cause must be chosen or newly created. + */ + const esp_err_t error; }; /** diff --git a/examples/cxx/experimental/experimental_cpp_component/test/test_cxx_exceptions.cpp b/examples/cxx/experimental/experimental_cpp_component/test/test_cxx_exceptions.cpp index c4e16cd27e..4a36f84fda 100644 --- a/examples/cxx/experimental/experimental_cpp_component/test/test_cxx_exceptions.cpp +++ b/examples/cxx/experimental/experimental_cpp_component/test/test_cxx_exceptions.cpp @@ -1,4 +1,5 @@ #include +#include #include "unity.h" #include "unity_cxx.hpp" @@ -48,5 +49,14 @@ TEST_CASE("CHECK_THROW throws", "[cxx exception][leaks=" LEAKS "]") TEST_THROW(CHECK_THROW(error), ESPException); } +TEST_CASE("ESPException has working what() method", "[cxx exception][leaks=" LEAKS "]") +{ + try { + throw ESPException(ESP_FAIL); + } catch (ESPException &e) { + TEST_ASSERT(strcmp(esp_err_to_name(ESP_FAIL), e.what()) == 0); + } +} + #endif // __cpp_exceptions diff --git a/examples/cxx/experimental/experimental_cpp_component/test/test_esp_event_cxx.cpp b/examples/cxx/experimental/experimental_cpp_component/test/test_esp_event_cxx.cpp new file mode 100644 index 0000000000..e7f3efc965 --- /dev/null +++ b/examples/cxx/experimental/experimental_cpp_component/test/test_esp_event_cxx.cpp @@ -0,0 +1,762 @@ +#include +#include +#include +#include +#include +#include +#include "unity.h" +#include "unity_cxx.hpp" +#include "esp_timer.h" +#include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +#include "esp_event_cxx.hpp" +#include "esp_event_api.hpp" + +#include "esp_exception.hpp" + +#ifdef __cpp_exceptions + +#ifdef CONFIG_ESP_TIMER_PROFILING +#define WITH_PROFILING 1 +#endif + +using namespace idf::event; +using namespace std; + +ESP_EVENT_DEFINE_BASE(TEST_EVENT_BASE_0); +ESP_EVENT_DEFINE_BASE(TEST_EVENT_BASE_1); +static ESPEventID TEST_EVENT_ID_0(0); +static ESPEventID TEST_EVENT_ID_1(1); + +#define TAG "Event CXX Test" + +ESPEvent TEMPLATE_EVENT_0(TEST_EVENT_BASE_0, TEST_EVENT_ID_0); +ESPEvent TEMPLATE_EVENT_1(TEST_EVENT_BASE_0, TEST_EVENT_ID_1); + +/** + * Mock which only returns a certain error message. + */ +class ESPEventMock : public ESPEventAPIDefault { +public: + esp_err_t next_error; + + esp_err_t handler_register(esp_event_base_t event_base, + int32_t event_id, + esp_event_handler_t event_handler, + void* event_handler_arg, + esp_event_handler_instance_t *instance) override { + return next_error; + } + + esp_err_t handler_unregister(esp_event_base_t event_base, + int32_t event_id, + esp_event_handler_instance_t instance) override { + return next_error; + } + + esp_err_t post(esp_event_base_t event_base, + int32_t event_id, + void* event_data, + size_t event_data_size, + TickType_t ticks_to_wait) override { + return next_error; + } +}; + +/* The initial logging "initializing test" is to ensure mutex allocation is not counted against memory not being freed + * during teardown. + * esp_event_loop_delete_default() tries to mitigate side effects of failed tests where objects + * with automatic storage duration weren't destructed. + * + * TODO: The final "testing mem..." is to prevent memory leaks which occur for yet unknown reasons + */ +struct EventFixture { + EventFixture() : free_mem_before(0) { + ESP_LOGI(TAG, "initializing test"); + + esp_event_loop_delete_default(); + + free_mem_before = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); + } + + virtual ~EventFixture() + { + ESP_LOGI(TAG, "de-initializing test..."); + } + + size_t free_mem_before; +}; + +struct EventLoopFix : public EventFixture { + EventLoopFix() + : EventFixture(), + api(new ESPEventAPIDefault()), + event_loop(api), + ev0_called(false), + ev1_called(false), + timeout(false), + ev0(), + ev1() + { + handler0 = [this](const ESPEvent& ev, const void* data) { + ev0 = ev; + ev0_called = true; + }; + + handler1 = [this](const ESPEvent& ev, const void* data) { + ev1 = ev; + ev1_called = true; + }; + + timer_cb = [this](const ESPEvent& ev) { + timeout_event = ev; + timeout = true; + }; + } + + std::function handler0; + + std::function handler1; + + std::function timer_cb; + + std::shared_ptr api; + ESPEventLoop event_loop; + bool ev0_called; + bool ev1_called; + bool timeout; + ESPEvent ev0; + ESPEvent ev1; + ESPEvent timeout_event; +}; + +void send_default_event(ESPEventID event_id = TEST_EVENT_ID_0) { + TEST_ASSERT_EQUAL(ESP_OK, esp_event_post(TEST_EVENT_BASE_0, + event_id.get_id(), + nullptr, + 0, + portMAX_DELAY)); +} + +TEST_CASE("ESPEventAPIDefault deinitialization without failure", "[cxx event]") +{ + EventFixture f; + std::shared_ptr api(new ESPEventAPIDefault()); + esp_event_loop_delete_default(); + + // destructor of ESPEventAPI needs to run without failure +} + +TEST_CASE("ESPEventReg cb nullptr", "[cxx event]") +{ + EventFixture f; + std::shared_ptr api(new ESPEventAPIDefault()); + ESPEventLoop event_loop(api); + TEST_THROW(ESPEventReg reg(nullptr, TEMPLATE_EVENT_0, api), EventException); +} + +TEST_CASE("ESPEventReg api nullptr", "[cxx event]") +{ + EventFixture f; + function cb = [](const ESPEvent &event, const void *data) {}; + shared_ptr api(new ESPEventAPIDefault()); + ESPEventLoop event_loop(api); + TEST_THROW(ESPEventReg reg(cb, TEMPLATE_EVENT_0, nullptr), EventException); +} + +TEST_CASE("ESPEventReg event api not initialized", "[cxx event]") +{ + EventFixture f; + + std::shared_ptr api(new ESPEventMock()); + api->next_error = ESP_ERR_INVALID_STATE; + TEST_THROW(ESPEventReg cb([](const ESPEvent &, const void* data) { }, TEMPLATE_EVENT_0, api), + ESPEventRegisterException); +} + +TEST_CASE("ESPEventReg event register failure no loop initialized", "[cxx event]") +{ + EventFixture f; + // registering will fail because default event loop isn't initialized + std::shared_ptr api(new ESPEventAPIDefault()); + esp_event_loop_delete_default(); + TEST_THROW(ESPEventReg cb([](const ESPEvent &, const void* data) { }, TEMPLATE_EVENT_0, api), + ESPEventRegisterException); +} + +TEST_CASE("ESPEventReg initialization failure", "[cxx event]") +{ + ESPEvent event; + EventFixture f; + std::shared_ptr api = std::make_shared(); + + TEST_THROW(ESPEventReg([&](const ESPEvent &ev, const void*) { event = ev; }, ESPEvent(), api), + ESPEventRegisterException); +} + +TEST_CASE("ESPEventReg registration success", "[cxx event]") +{ + ESPEvent event; + EventFixture f; + std::shared_ptr api = std::make_shared(); + ESPEventLoop loop(api); + + ESPEventReg registration([&event](const ESPEvent &ev, const void *) { event = ev; }, TEMPLATE_EVENT_0, api); + send_default_event(); + + TEST_ASSERT(event == TEMPLATE_EVENT_0); +} + +TEST_CASE("ESPEventLoopCB event passes data", "[cxx event]") +{ + EventLoopFix fix; + int data_sent = 47; + int data_received = 0; + + ESPEvent event; + + ESPEventReg cb([&event, &data_received](const ESPEvent & ev, const void* data) { + event = ev; + data_received = *((int*) data); + }, TEMPLATE_EVENT_0, fix.api); + + fix.event_loop.post_event_data(ESPEvent(TEST_EVENT_BASE_0, TEST_EVENT_ID_0), data_sent); + + TEST_ASSERT(TEMPLATE_EVENT_0 == event); + TEST_ASSERT(data_sent == data_received); +} + +TEST_CASE("ESPEventLoop Create event loop failure", "[cxx event]") +{ + EventFixture f; + esp_event_loop_create_default(); + + TEST_THROW(ESPEventLoop event_loop, EventException); + + // just in case + esp_event_loop_delete_default(); +} + +TEST_CASE("ESPEventLoop registration invalid event callback", "[cxx event]") +{ + EventFixture f; + + ESPEventLoop event_loop; + std::function event_cb; + TEST_THROW(event_loop.register_event(TEMPLATE_EVENT_0, event_cb), EventException); +} + +TEST_CASE("ESPEventLoop timed registration invalid event callback", "[cxx event]") +{ + EventFixture f; + + ESPEventLoop event_loop; + std::function event_cb; + std::function timer_cb = [](const ESPEvent &ev) { }; + TEST_THROW(event_loop.register_event_timed(TEMPLATE_EVENT_0, event_cb, std::chrono::microseconds(10), timer_cb), + EventException); +} + +TEST_CASE("ESPEventLoop timed registration invalid timeout callback", "[cxx event]") +{ + EventFixture f; + + ESPEventLoop event_loop; + std::function event_cb = [](const ESPEvent &ev, const void *data) { }; + std::function timer_cb; + TEST_THROW(event_loop.register_event_timed(TEMPLATE_EVENT_0, event_cb, std::chrono::microseconds(10), timer_cb), + EventException); +} + +TEST_CASE("ESPEventLoop make sure timeout is off after register exception", "[cxx event]") +{ + EventFixture f; + ESPEvent timeout_event; + bool timeout = false; + + ESPEventLoop event_loop; + std::function event_cb = [&](const ESPEvent &ev, const void *data) { + timeout_event = ev; + }; + std::function timer_cb = [&](const ESPEvent& ev) { + timeout_event = ev; + timeout = true; + }; + esp_event_loop_delete_default(); + + // Below ~35 microseconds the timer expires too fast for esp_timer_stop() to prevent it from being called. + TEST_THROW(event_loop.register_event_timed(TEMPLATE_EVENT_0, event_cb, std::chrono::microseconds(40), timer_cb), + ESPEventRegisterException); + + TEST_ASSERT_EQUAL(false, timeout); + TEST_ASSERT(timeout_event == ESPEvent()); +} + +TEST_CASE("ESPEventLoop Delete event loop failure - no error", "[cxx event]") +{ + EventFixture f; + + ESPEventLoop event_loop; + + esp_event_loop_delete_default(); + + // destructor of ESPEventLoop needs to run without failure +} + +TEST_CASE("ESPEventLoop post nullptr event without registrations", "[cxx event]") +{ + EventFixture f; + + ESPEventLoop event_loop; + ESPEvent event(TEST_EVENT_BASE_0, TEST_EVENT_ID_0); + void *ptr = nullptr; + event_loop.post_event_data(event, ptr); +} + +TEST_CASE("ESPEventLoop post int event without registrations", "[cxx event]") +{ + EventFixture f; + + ESPEventLoop event_loop; + ESPEvent event(TEST_EVENT_BASE_0, TEST_EVENT_ID_0); + int fourtyseven = 47; + event_loop.post_event_data(event, fourtyseven); +} + +TEST_CASE("ESPEventLoop can create, use and delete ESPEventLoop", "[cxx event]") +{ + EventLoopFix fix; + bool tested = false; + + std::function cb = [&tested](const ESPEvent& event, const void* data) { + tested = true; + }; + + ESPEventReg registration(fix.handler0, TEMPLATE_EVENT_0, fix.api); + + void *ptr = nullptr; + fix.event_loop.post_event_data(ESPEvent(TEST_EVENT_BASE_0, TEST_EVENT_ID_0), ptr); + + TEST_ASSERT_EQUAL(true, fix.ev0_called); +} + +TEST_CASE("ESPEventLoop Register, receive, unregister ESPEvent", "[cxx event]") +{ + EventLoopFix fix; + + std::unique_ptr registration(new ESPEventReg(fix.handler0, TEMPLATE_EVENT_0, fix.api)); + + send_default_event(); + + TEST_ASSERT(fix.ev0 == TEMPLATE_EVENT_0); + + registration.reset(); + fix.ev0 = ESPEvent(); + + send_default_event(); + + TEST_ASSERT(fix.ev0 == ESPEvent()); +} + +TEST_CASE("ESPEventLoop register multiple ESPEvents, same cb", "[cxx event]") +{ + EventLoopFix fix; + + ESPEventReg registration0(fix.handler0, TEMPLATE_EVENT_0, fix.api); + ESPEventReg registration1(fix.handler1, TEMPLATE_EVENT_1, fix.api); + + send_default_event(); + + TEST_ASSERT(fix.ev0 == TEMPLATE_EVENT_0); + fix.ev0 = ESPEvent(); + + send_default_event(TEST_EVENT_ID_1); + + TEST_ASSERT(fix.ev1 == TEMPLATE_EVENT_1); +} + +TEST_CASE("ESPEventLoop register multiple ESPEvents, multiple cbs", "[cxx event]") +{ + EventLoopFix fix; + + ESPEventReg registration0(fix.handler0, TEMPLATE_EVENT_0, fix.api); + ESPEventReg registration1(fix.handler1, TEMPLATE_EVENT_1, fix.api); + + send_default_event(); + send_default_event(TEST_EVENT_ID_1); + + TEST_ASSERT(fix.ev0 == TEMPLATE_EVENT_0); + TEST_ASSERT(fix.ev1 == TEMPLATE_EVENT_1); +} + +TEST_CASE("ESPEventLoop register to all events of one event base", "[cxx event]") +{ + EventLoopFix fix; + + ESPEvent any_id_event(ESP_EVENT_ANY_BASE, ESPEventID(ESP_EVENT_ANY_ID)); + ESPEventReg registration(fix.handler0, any_id_event, fix.api); + + send_default_event(); + + TEST_ASSERT(fix.ev0 == TEMPLATE_EVENT_0); + + send_default_event(TEST_EVENT_ID_1); + + TEST_ASSERT(fix.ev0 == TEMPLATE_EVENT_1); +} + +TEST_CASE("ESPEventLoop register to all ESP events", "[cxx event]") +{ + EventLoopFix fix; + + ESPEvent any_event(ESP_EVENT_ANY_BASE, ESPEventID(ESP_EVENT_ANY_ID)); + ESPEventReg registration(fix.handler0, any_event, fix.api); + + send_default_event(); + + TEST_ASSERT(fix.ev0 == TEMPLATE_EVENT_0); + + send_default_event(TEST_EVENT_ID_1); + + TEST_ASSERT(fix.ev0 == TEMPLATE_EVENT_1); + + void *ptr = nullptr; + fix.event_loop.post_event_data(ESPEvent(TEST_EVENT_BASE_1, TEST_EVENT_ID_0), ptr); + + // check reception of event with different base + TEST_ASSERT_EQUAL(TEST_EVENT_BASE_1, fix.ev0.base); + TEST_ASSERT_EQUAL(TEST_EVENT_ID_0.get_id(), fix.ev0.id.get_id()); +} + +TEST_CASE("ESPEventLoop direct register, receive, unregister ESPEvent", "[cxx event]") +{ + EventLoopFix fix; + + std::unique_ptr registration = fix.event_loop.register_event(TEMPLATE_EVENT_0, fix.handler0); + + send_default_event(); + + TEST_ASSERT(fix.ev0 == TEMPLATE_EVENT_0); + + registration.reset(); + fix.ev0 = ESPEvent(); + + send_default_event(); + + TEST_ASSERT(fix.ev0 == ESPEvent()); +} + +TEST_CASE("ESPEventLoop set timeout invalid timeout", "[cxx event]") +{ + EventLoopFix fix; + + const std::chrono::microseconds INVALID_US(MIN_TIMEOUT - chrono::microseconds(1)); + + TEST_THROW(ESPEventRegTimed(fix.handler0, TEMPLATE_EVENT_0, fix.timer_cb, INVALID_US, fix.api), + EventException); +} + +TEST_CASE("ESPEventLoop lonely timeout", "[cxx event]") +{ + EventLoopFix fix; + + ESPEventRegTimed timed_reg(fix.handler0, TEMPLATE_EVENT_0, fix.timer_cb, MIN_TIMEOUT, fix.api); + + vTaskDelay(10 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL(true, fix.timeout); + TEST_ASSERT_EQUAL(false, fix.ev0_called); +} + +TEST_CASE("ESPEventLoop timeout unregisters from loop", "[cxx event]") +{ + EventLoopFix fix; + + ESPEventRegTimed timed_reg(fix.handler0, TEMPLATE_EVENT_0, fix.timer_cb, MIN_TIMEOUT, fix.api); + + vTaskDelay(10 / portTICK_PERIOD_MS); + + send_default_event(TEST_EVENT_ID_0); + + TEST_ASSERT_EQUAL(true, fix.timeout); + TEST_ASSERT_EQUAL(false, fix.ev0_called); +} + +TEST_CASE("ESPEventLoop no timeout", "[cxx event]") +{ + EventLoopFix fix; + + ESPEventRegTimed timed_reg(fix.handler0, TEMPLATE_EVENT_0, fix.timer_cb, std::chrono::microseconds(500000), fix.api); + + vTaskDelay(10 / portTICK_PERIOD_MS); + send_default_event(); + + TEST_ASSERT_EQUAL(false, fix.timeout); + TEST_ASSERT_EQUAL(true, fix.ev0_called); +} + + +/** + * Registers an event via both set_timeout() and register_event(). + * Result: both handlers will be invoked, the timeout callback won't be called. + */ +TEST_CASE("ESPEventLoop register timeout and event - no timeout", "[cxx event]") +{ + EventLoopFix fix; + + ESPEventReg reg(fix.handler0, TEMPLATE_EVENT_0, fix.api); + ESPEventRegTimed timed_reg(fix.handler1, TEMPLATE_EVENT_0, fix.timer_cb, std::chrono::microseconds(500000), fix.api); + + send_default_event(); + + TEST_ASSERT(fix.ev0 == TEMPLATE_EVENT_0); + TEST_ASSERT(fix.ev1 == TEMPLATE_EVENT_0); + TEST_ASSERT_EQUAL(false, fix.timeout); +} + +/** + * Registers an event via both set_timeout() and register_event(). + * Result: both handlers will be invoked, the timeout callback won't be called. + */ +TEST_CASE("ESPEventLoop direct register timeout and event - no timeout", "[cxx event]") +{ + EventLoopFix fix; + + unique_ptr reg = fix.event_loop.register_event(TEMPLATE_EVENT_0, fix.handler0); + unique_ptr timed_reg = fix.event_loop.register_event_timed(TEMPLATE_EVENT_0, + fix.handler1, + std::chrono::microseconds(500000), + fix.timer_cb); + + send_default_event(); + + TEST_ASSERT(fix.ev0 == TEMPLATE_EVENT_0); + TEST_ASSERT(fix.ev1 == TEMPLATE_EVENT_0); + TEST_ASSERT_EQUAL(false, fix.timeout); +} + +/** + * Registers an event via both set_timeout() and register_event(). + * Result: both handlers will be invoked, the timeout callback won't be called. + */ +TEST_CASE("ESPEventLoop register timeout and event - timeout", "[cxx event]") +{ + EventLoopFix fix; + + ESPEventReg reg(fix.handler0, TEMPLATE_EVENT_0, fix.api); + ESPEventRegTimed timed_reg(fix.handler1, TEMPLATE_EVENT_0, fix.timer_cb, MIN_TIMEOUT, fix.api); + + vTaskDelay(10 / portTICK_PERIOD_MS); + send_default_event(); + + TEST_ASSERT(fix.ev0 == TEMPLATE_EVENT_0); + TEST_ASSERT_EQUAL(false, fix.ev1_called); + TEST_ASSERT(fix.timeout_event == TEMPLATE_EVENT_0); + TEST_ASSERT_EQUAL(true, fix.timeout); +} + +TEST_CASE("ESPEventLoop custom loop register, receive, unregister ESPEvent", "[cxx event]") +{ + EventFixture f; + ESPEvent event; + esp_event_loop_args_t loop_args; + loop_args.queue_size = 32; + loop_args.task_name = "sys_evt"; + loop_args.task_stack_size = 2304; + loop_args.task_priority = 20; + loop_args.task_core_id = 0; + + std::shared_ptr api(new ESPEventAPICustom(loop_args)); + + ESPEventLoop event_loop(api); + std::function cb = [&event](const ESPEvent& ev, const void* data) { + event = ev; + }; + shared_ptr registration = event_loop.register_event(TEMPLATE_EVENT_0, cb); + + void *ptr = nullptr; + event_loop.post_event_data(ESPEvent(TEST_EVENT_BASE_0, TEST_EVENT_ID_0), ptr); + + ESP_ERROR_CHECK(api->run(1)); + + TEST_ASSERT(event == TEMPLATE_EVENT_0); + + registration.reset(); + event = ESPEvent(); + + event_loop.post_event_data(ESPEvent(TEST_EVENT_BASE_0, TEST_EVENT_ID_0), ptr); + + ESP_ERROR_CHECK(api->run(1)); + + TEST_ASSERT(event == ESPEvent()); +} + +TEST_CASE("ESPEventHandlerSync simple construction and destruction", "[cxx event]") +{ + EventFixture f; + ESPEventHandlerSync handler(make_shared()); + handler.listen_to(TEMPLATE_EVENT_0); +} + +TEST_CASE("ESPEventHandlerSync simple event wait", "[cxx event]") +{ + EventFixture f; + ESPEventHandlerSync handler(make_shared()); + handler.listen_to(TEMPLATE_EVENT_0); + + send_default_event(); + + ESPEventHandlerSync::EventResult result = handler.wait_event(); + + TEST_ASSERT_EQUAL(TEMPLATE_EVENT_0.base, result.event.base); + TEST_ASSERT_EQUAL(TEMPLATE_EVENT_0.id.get_id(), result.event.id.get_id()); +} + +TEST_CASE("ESPEventHandlerSync wait_for(0) succeed", "[cxx event]") +{ + EventFixture f; + ESPEventHandlerSync handler(make_shared()); + handler.listen_to(TEMPLATE_EVENT_0); + + send_default_event(); + + ESPEventHandlerSync::EventResult result = handler.wait_event_for(chrono::milliseconds(0)); + TEST_ASSERT(TEMPLATE_EVENT_0 == result.event); +} + +TEST_CASE("ESPEventHandlerSync start waiting after events arrived", "[cxx event]") +{ + EventFixture f; + ESPEventHandlerSync handler(make_shared()); + handler.listen_to(TEMPLATE_EVENT_0); + + send_default_event(); + send_default_event(); + + TEST_ASSERT(handler.wait_event().event == TEMPLATE_EVENT_0); + TEST_ASSERT(handler.wait_event().event == TEMPLATE_EVENT_0); +} + +// helper function to post events, simulating an event source +void post_events(int event_num) { + for (int i = 0; i < event_num; i++) { + ESP_ERROR_CHECK(esp_event_post(TEST_EVENT_BASE_0, + TEST_EVENT_ID_0.get_id(), + nullptr, + 0, + portMAX_DELAY)); + vTaskDelay(10 / portTICK_PERIOD_MS); + } +} + +TEST_CASE("ESPEventHandlerSync simultaneous event handling", "[cxx event]") +{ + EventFixture f; + // Create handler with queue size 1 + ESPEventHandlerSync handler(make_shared()); + handler.listen_to(TEMPLATE_EVENT_0); + thread th(post_events, 3); + + // no for-loop for better feedback (line numbers) + TEST_ASSERT(handler.wait_event().event == TEMPLATE_EVENT_0); + TEST_ASSERT(handler.wait_event().event == TEMPLATE_EVENT_0); + TEST_ASSERT(handler.wait_event().event == TEMPLATE_EVENT_0); + + TEST_ASSERT_EQUAL(0, handler.get_send_queue_errors()); + th.join(); +} + +TEST_CASE("ESPEventHandlerSync wait_for(0) timeout", "[cxx event]") +{ + EventFixture f; + ESPEventHandlerSync handler(make_shared()); + + ESPEventHandlerSync::EventResultTimed result = handler.wait_event_for(chrono::milliseconds(0)); + TEST_ASSERT_EQUAL(true, result.timeout); +} + +TEST_CASE("ESPEventHandlerSync register default event fails", "[cxx event]") +{ + EventFixture f; + ESPEventHandlerSync handler(make_shared()); + TEST_THROW(handler.listen_to(ESPEvent()), EventException); +} + +TEST_CASE("ESPEventHandlerSync null pointer", "[cxx event]") +{ + EventFixture f; + TEST_THROW(ESPEventHandlerSync handler(nullptr), EventException); +} + +TEST_CASE("ESPEventHandlerSync empty shared_ptr", "[cxx event]") +{ + EventFixture f; + shared_ptr event_loop; + TEST_THROW(ESPEventHandlerSync handler(event_loop), EventException); +} + +TEST_CASE("ESPEventHandlerSync queue size 0", "[cxx event]") +{ + EventFixture f; + + TEST_THROW(ESPEventHandlerSync handler(make_shared(), 0), EventException); +} + +TEST_CASE("ESPEventHandlerSync receive after timeout", "[cxx event]") +{ + EventFixture f; + ESPEventHandlerSync handler(make_shared()); + handler.listen_to(TEMPLATE_EVENT_0); + + TEST_ASSERT_EQUAL(true, handler.wait_event_for(chrono::milliseconds(0)).timeout); + + send_default_event(); + + ESPEvent event = handler.wait_event().event; + TEST_ASSERT(TEMPLATE_EVENT_0 == event); +} + +TEST_CASE("ESPEventHandlerSync send too many events", "[cxx event]") +{ + EventFixture f; + // Create handler with queue size 1 + ESPEventHandlerSync handler(make_shared(), 1); + handler.listen_to(TEMPLATE_EVENT_0); + TEST_ASSERT_EQUAL(0, handler.get_send_queue_errors()); + + send_default_event(); + send_default_event(); + TEST_ASSERT(handler.wait_event().event == TEMPLATE_EVENT_0); + TEST_ASSERT_EQUAL(true, handler.wait_event_for(chrono::milliseconds(10)).timeout); + TEST_ASSERT_EQUAL(1, handler.get_send_queue_errors()); +} + +TEST_CASE("ESPEventAPIDefault initialization failure", "[cxx event]") +{ + EventFixture f; + esp_event_loop_create_default(); + TEST_THROW(std::shared_ptr api(new ESPEventAPIDefault()), EventException); + esp_event_loop_delete_default(); +} + +TEST_CASE("ESPEventAPICustom no mem", "[cxx event]") +{ + EventFixture f; + esp_event_loop_args_t loop_args; + loop_args.queue_size = 1000000; + loop_args.task_name = "custom_evt"; + loop_args.task_stack_size = 2304; + loop_args.task_priority = 20; + loop_args.task_core_id = 0; + + esp_event_loop_create_default(); + + TEST_THROW(std::shared_ptr api(new ESPEventAPICustom(loop_args)), EventException); + + esp_event_loop_delete_default(); +} + +#endif // __cpp_exceptions + diff --git a/examples/cxx/experimental/experimental_cpp_component/test/unity_cxx.hpp b/examples/cxx/experimental/experimental_cpp_component/test/unity_cxx.hpp index f905332385..c9058c7dc0 100644 --- a/examples/cxx/experimental/experimental_cpp_component/test/unity_cxx.hpp +++ b/examples/cxx/experimental/experimental_cpp_component/test/unity_cxx.hpp @@ -1,9 +1,8 @@ -#ifndef UNITY_CXX_H_ -#define UNITY_CXX_H_ +#pragma once #include "unity.h" -#define STR(x) #x +#define CXX_UNITY_TYPE_TO_STR(x) #x /** * Very simple helper macro to catch exceptions. @@ -24,12 +23,10 @@ } catch ( std::exception &e) { \ caught_different = true; \ } \ - TEST_ASSERT_FALSE_MESSAGE(caught_different, "ERROR: Expected " STR(exception_) \ + TEST_ASSERT_FALSE_MESSAGE(caught_different, "ERROR: Expected " CXX_UNITY_TYPE_TO_STR(exception_) \ ", but caught different exception."); \ - TEST_ASSERT_TRUE_MESSAGE(caught, "ERROR: Expected " STR(exception_) \ + TEST_ASSERT_TRUE_MESSAGE(caught, "ERROR: Expected " CXX_UNITY_TYPE_TO_STR(exception_) \ ", but no exception thrown."); \ } \ while (0) - -#endif // UNITY_CXX_H_ diff --git a/examples/cxx/experimental/sensor_mcp9808/README.md b/examples/cxx/experimental/sensor_mcp9808/README.md index b980bec467..2989cb4ee5 100644 --- a/examples/cxx/experimental/sensor_mcp9808/README.md +++ b/examples/cxx/experimental/sensor_mcp9808/README.md @@ -43,7 +43,7 @@ Current temperature: 24.875 If something went wrong: ``` -I2C Exception with error: -1 +I2C Exception with error: ESP_FAIL (-1) Coulnd't read sensor! ``` diff --git a/examples/cxx/experimental/sensor_mcp9808/main/sensor_mcp9808.cpp b/examples/cxx/experimental/sensor_mcp9808/main/sensor_mcp9808.cpp index cd3dc59f9b..21139819e9 100644 --- a/examples/cxx/experimental/sensor_mcp9808/main/sensor_mcp9808.cpp +++ b/examples/cxx/experimental/sensor_mcp9808/main/sensor_mcp9808.cpp @@ -38,7 +38,8 @@ extern "C" void app_main(void) cout << "Current temperature: " << calc_temp(data[0], data[1]) << endl; } catch (const I2CException &e) { - cout << "I2C Exception with error: " << e.error << endl; + cout << "I2C Exception with error: " << e.what(); + cout << " (" << e.error<< ")" << endl; cout << "Coulnd't read sensor!" << endl; } } diff --git a/tools/ci/config/target-test.yml b/tools/ci/config/target-test.yml index 7c3183a9d0..3c898d9b0c 100644 --- a/tools/ci/config/target-test.yml +++ b/tools/ci/config/target-test.yml @@ -429,7 +429,7 @@ component_ut_test_001: UT_001: extends: .unit_test_template - parallel: 43 + parallel: 44 tags: - ESP32_IDF - UT_T1_1 @@ -644,7 +644,7 @@ UT_045: - ESP32_IDF - UT_SDIO - psram - + UT_046: extends: .unit_test_template tags: diff --git a/tools/unit-test-app/CMakeLists.txt b/tools/unit-test-app/CMakeLists.txt index 24351c17bc..f25adef66b 100644 --- a/tools/unit-test-app/CMakeLists.txt +++ b/tools/unit-test-app/CMakeLists.txt @@ -2,7 +2,7 @@ # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) -list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component/" +list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component" "$ENV{IDF_PATH}/examples/peripherals/rmt/ir_protocols/components") include($ENV{IDF_PATH}/tools/cmake/project.cmake)