├── .github └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── Docs ├── Coroutines from C++20 on ARM Cortex-M.md ├── CoroutinesFirstStage.md ├── DisplayGC9A01NormalTrace.pvs ├── DisplayGC9A01NormalTrace.sr └── LvglWidgetsDiagram.drawio ├── Features_list.md ├── Firmware ├── .clang-format ├── .vscode │ ├── .cortex-debug.peripherals.state.json │ ├── .cortex-debug.registers.state.json │ ├── c_cpp_properties.json │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── 3rdparty │ ├── CMakeLists.txt │ └── cppcoro_lib │ │ └── CMakeLists.txt ├── CMakeLists.txt ├── CMakeSettings.json ├── ap_application.cpp ├── ap_application.hpp ├── cmake │ ├── arm-gcc-toolchain.cmake │ ├── base.cmake │ ├── jlink │ │ ├── commandline.txt │ │ ├── runJLinkExe-nrf52 │ │ ├── runJLinkGDBServer-nrf52 │ │ └── runJLinkRTTClient │ └── nordic │ │ ├── CMakeLists.txt │ │ └── nordic_postbuild.cmake ├── conanfile.txt ├── drivers │ ├── CMakeLists.txt │ ├── ble │ │ ├── CMakeLists.txt │ │ ├── ble_battery_service.cpp │ │ ├── ble_custom_service.cpp │ │ ├── ble_datetime_service.cpp │ │ ├── ble_desktop_softdevice.cpp │ │ ├── ble_heartrate_service.cpp │ │ ├── ble_softdevice.cpp │ │ └── inc │ │ │ └── ble │ │ │ ├── desktop_ble │ │ │ └── ble_desktop_softdevice.hpp │ │ │ └── nordic_ble │ │ │ ├── ble_battery_service.hpp │ │ │ ├── ble_custom_service.hpp │ │ │ ├── ble_datetime_service.hpp │ │ │ ├── ble_heartrate_service.hpp │ │ │ ├── ble_softdevice.hpp │ │ │ └── ble_stack_constants.hpp │ ├── board │ │ ├── CMakeLists.txt │ │ ├── inc │ │ │ └── board │ │ │ │ ├── hardware_usings.hpp │ │ │ │ └── watchboard.hpp │ │ └── watchboard.cpp │ ├── buttons │ │ ├── CMakeLists.txt │ │ ├── bt_firmware_simulator_hardware_buttons_manual_backend.cpp │ │ ├── bt_nordic_hardware_backend.cpp │ │ ├── bt_os_stub_hardware_buttons_backend.cpp │ │ └── inc │ │ │ └── buttons │ │ │ ├── bt_firmware_simulator_hardware_buttons_manual_backend.hpp │ │ │ └── bt_nordic_hardware_backend.hpp │ ├── display │ │ ├── CMakeLists.txt │ │ └── inc │ │ │ └── display │ │ │ ├── display_coro_compact_gc9a01.hpp │ │ │ └── display_spi_common_coro.hpp │ ├── factory_impl │ │ ├── CMakeLists.txt │ │ ├── ble_services_stub.cpp │ │ ├── ble_services_stub.hpp │ │ ├── ih_ble_service_factory.cpp │ │ └── ih_ible_softdevice.cpp │ ├── gpio │ │ ├── CMakeLists.txt │ │ ├── gpio_pin.cpp │ │ └── inc │ │ │ └── gpio │ │ │ └── gpio_pin.hpp │ ├── headers │ │ ├── CMakeLists.txt │ │ └── ih │ │ │ └── drivers │ │ │ ├── ih_ble_battery_service.hpp │ │ │ ├── ih_ble_dts_service.hpp │ │ │ ├── ih_ble_heartrate_service.hpp │ │ │ ├── ih_ble_service_factory.hpp │ │ │ ├── ih_button_driver.hpp │ │ │ ├── ih_ible_softdevice.hpp │ │ │ └── transaction_item.hpp │ ├── i2c │ │ ├── CMakeLists.txt │ │ ├── i2c_test.cpp │ │ └── inc │ │ │ └── i2c │ │ │ └── i2c_test.hpp │ ├── platform_delay │ │ ├── CMakeLists.txt │ │ ├── delay_provider.cpp │ │ └── inc │ │ │ └── delay │ │ │ └── delay_provider.hpp │ ├── spi │ │ ├── CMakeLists.txt │ │ └── inc │ │ │ ├── backends │ │ │ ├── spi_backend_desktop.hpp │ │ │ └── spi_backend_nrf.hpp │ │ │ └── spi │ │ │ ├── spi_wrapper.hpp │ │ │ └── spi_wrapper_async_templated.hpp │ └── winbondflash │ │ ├── CMakeLists.txt │ │ └── inc │ │ └── windbondflash │ │ ├── winbond_commandset.hpp │ │ └── winbond_flash_templated.hpp ├── firmware_tests │ ├── CMakeLists.txt │ ├── article_example │ │ ├── CMakeLists.txt │ │ ├── coroutine_utils.hpp │ │ ├── display_common_coroutine.hpp │ │ ├── display_gc9a01.hpp │ │ ├── gpio.hpp │ │ ├── root.cpp │ │ ├── spi_desktop_backend.hpp │ │ └── spi_driver_high_level.hpp │ ├── buttons_driver │ │ ├── buttons_driver_test.cpp │ │ ├── buttons_fake_backends.hpp │ │ ├── buttons_fake_event_handler.hpp │ │ └── buttons_fixture.hpp │ ├── clock_page_handler │ │ ├── clock_page_handler_fixture.hpp │ │ └── clock_page_view_handler_test.cpp │ ├── coroutine │ │ ├── .vscode │ │ │ ├── launch.json │ │ │ └── settings.json │ │ ├── CMakeLists.txt │ │ ├── coroutine_thoughts.cpp │ │ ├── st7789_draft.hpp │ │ └── thoughts.hpp │ ├── drivers │ │ ├── spi │ │ │ ├── mock_gpio.hpp │ │ │ ├── mock_spi.hpp │ │ │ ├── spi_driver_fixture.hpp │ │ │ ├── spi_driver_test_suite.cpp │ │ │ └── spi_fake_backend.hpp │ │ └── windond_flash │ │ │ ├── flash_driver_test_suite.cpp │ │ │ └── flash_fixture.hpp │ ├── main_window │ │ ├── mainwindow_fakes.hpp │ │ ├── mainwindow_fixture.hpp │ │ └── mainwindow_model_test.cpp │ └── stubs │ │ ├── base_mocked_page.hpp │ │ ├── base_stub_widget.hpp │ │ ├── interfaces │ │ └── spi │ │ │ └── mocked_spi.hpp │ │ ├── pages │ │ ├── clock_watch_fake_view.hpp │ │ ├── health_watch_fake_view.hpp │ │ └── player_watch_fake_view.hpp │ │ ├── pages_stub_pages_creator.cpp │ │ ├── pages_stub_pages_creator.hpp │ │ ├── widgets │ │ ├── battery_widget_fake.hpp │ │ ├── bluetooth_widget_fake.hpp │ │ └── pages_switch_widget_fake.hpp │ │ ├── widgets_stub_pages_creator.cpp │ │ └── widgets_stub_pages_creator.hpp ├── graphics │ ├── CMakeLists.txt │ ├── fonts │ │ ├── CMakeLists.txt │ │ ├── GeneratedFontello │ │ │ ├── IconFont16px.ttf │ │ │ ├── IconFont24px.ttf │ │ │ ├── IconFont35px.ttf │ │ │ ├── PlayerIcons.ttf │ │ │ ├── PlayerIconsCodes.JPG │ │ │ └── a_LCDNova.ttf │ │ ├── IconFont16px.cpp │ │ ├── IconFont16px.hpp │ │ ├── IconFont24px.cpp │ │ ├── IconFont24px.hpp │ │ ├── IconFont35px.cpp │ │ ├── IconFont35px.hpp │ │ ├── LcdNova12px.cpp │ │ ├── LcdNova16px.cpp │ │ ├── LcdNova24px.cpp │ │ ├── LcdNova30px.cpp │ │ ├── LcdNova36px.cpp │ │ ├── LcdNova68px.cpp │ │ ├── PlayerIcons68px.cpp │ │ └── PlayerIcons68px.hpp │ ├── gs_event_dispatcher.cpp │ ├── gs_event_dispatcher.hpp │ ├── gs_lvgl_service.cpp │ ├── gs_lvgl_service.hpp │ ├── gs_theme_controller.cpp │ ├── gs_theme_controller.hpp │ ├── icons │ │ ├── Footsteps_v2.svg │ │ ├── fontello.ttf │ │ └── icon.svg │ ├── ih │ │ ├── creators │ │ │ ├── gs_ipages_creator.hpp │ │ │ └── gs_iwidgets_creator.hpp │ │ ├── gs_events.hpp │ │ ├── gs_ievent_handler.hpp │ │ ├── gs_imain_window.hpp │ │ ├── gs_ipage_view_object.hpp │ │ ├── gs_itheme_controller.hpp │ │ ├── gs_iwidget_object.hpp │ │ ├── pages │ │ │ ├── gs_iclock_page_view.hpp │ │ │ ├── gs_ihealth_page_view.hpp │ │ │ └── gs_iplayer_page_view.hpp │ │ └── widgets │ │ │ ├── gs_ibattery_widget.hpp │ │ │ ├── gs_ibluetooth_widget.hpp │ │ │ └── gs_ipages_switch.hpp │ ├── lvgl_lib │ │ ├── CMakeLists.txt │ │ ├── GenerateCFilesList.txt │ │ ├── lvgl_driver_backend │ │ │ ├── CMakeLists.txt │ │ │ └── lv_drv_conf.h │ │ └── lvgl_library │ │ │ ├── CMakeLists.txt │ │ │ └── lv_conf.h │ ├── platform │ │ ├── CMakeLists.txt │ │ ├── gs_platform_layer.cpp │ │ └── gs_platform_layer.hpp │ └── widgets_layer │ │ ├── gs_event_handler_base.hpp │ │ ├── gs_main_window.cpp │ │ ├── gs_main_window.hpp │ │ ├── gs_main_window_event_handler.cpp │ │ ├── gs_main_window_event_handler.hpp │ │ ├── gs_main_window_view.cpp │ │ ├── gs_main_window_view.hpp │ │ ├── gs_page_view_object.cpp │ │ ├── gs_page_view_object.hpp │ │ ├── lvgl_ui.cpp │ │ ├── lvgl_ui.hpp │ │ ├── lvgl_views_creators │ │ ├── gs_pages_creator.cpp │ │ ├── gs_pages_creator.hpp │ │ ├── gs_widgets_creator.cpp │ │ └── gs_widgets_creator.hpp │ │ ├── pages │ │ ├── clock_page │ │ │ ├── gs_clock_page_handler.cpp │ │ │ ├── gs_clock_page_handler.hpp │ │ │ ├── gs_clock_page_view.cpp │ │ │ └── gs_clock_page_view.hpp │ │ ├── health_page │ │ │ ├── gs_health_page_view.cpp │ │ │ └── gs_health_page_view.hpp │ │ └── player_page │ │ │ ├── gs_player_page_view.cpp │ │ │ └── gs_player_page_view.hpp │ │ └── widgets │ │ ├── battery │ │ ├── gs_battery_handler.cpp │ │ ├── gs_battery_handler.hpp │ │ ├── gs_battery_widget.cpp │ │ └── gs_battery_widget.hpp │ │ ├── bluetooth │ │ ├── gs_bluetooth_widget.cpp │ │ ├── gs_bluetooth_widget.hpp │ │ ├── gs_bluetooth_widget_handler.cpp │ │ └── gs_bluetooth_widget_handler.hpp │ │ ├── gs_widget_base_obj.cpp │ │ ├── gs_widget_base_obj.hpp │ │ └── pages_switch │ │ ├── gs_pages_switch.cpp │ │ └── gs_pages_switch.hpp ├── logger │ ├── CMakeLists.txt │ ├── inc │ │ └── logger │ │ │ └── logger_service.hpp │ └── logger_service_impl.cpp ├── main.cpp ├── nrf52_headers │ ├── nrf52.h │ ├── nrf52.svd │ ├── nrf52_bitfields.h │ ├── nrf52_common.ld │ └── nrf52_name_change.h ├── old_toolchain_files │ ├── CMakeListsOld.txt │ ├── CMakeListsOldRoot.txt │ ├── CMakeListsSumlatorOld.txt │ ├── CMake_nRF5x.cmake │ └── arm-gcc-toolchain.cmake ├── sdk_dependent │ ├── board-pinout │ │ └── pca10040.h │ ├── config │ │ └── sdk_config.h │ └── gcc_nrf52.ld ├── service_providers │ ├── CMakeLists.txt │ ├── headers │ │ ├── CMakeLists.txt │ │ └── ih │ │ │ ├── sp_ibattery_service.hpp │ │ │ ├── sp_idatetime_service.hpp │ │ │ ├── sp_iheartrate_service.hpp │ │ │ └── sp_iservice_creator.hpp │ └── watch_fake_services │ │ ├── CMakeLists.txt │ │ ├── battery │ │ ├── CMakeLists.txt │ │ ├── sp_battery_service_fake.cpp │ │ └── sp_battery_service_fake.hpp │ │ ├── datetime │ │ ├── CMakeLists.txt │ │ ├── sp_datetime_service_fake.cpp │ │ └── sp_datetime_service_fake.hpp │ │ ├── heartrate │ │ ├── CMakeLists.txt │ │ ├── sp_heartrate_service_fake.cpp │ │ └── sp_heartrate_service_fake.hpp │ │ ├── inc │ │ └── sp_fake_services_creator.hpp │ │ └── sp_fake_services_creator.cpp └── utils │ ├── CMakeLists.txt │ ├── etl_profile.h │ └── inc │ └── utils │ ├── CallbackConnector.hpp │ ├── CoroUtils.hpp │ ├── CoroutineExample.hpp │ ├── FastPimpl.hpp │ ├── MetaUtils.hpp │ ├── Noncopyable.hpp │ ├── Platform.hpp │ ├── SimpleSignal.hpp │ ├── TimeWrapper.hpp │ └── coroutine │ ├── Common.hpp │ ├── Event.hpp │ ├── ExecutionQueueCoro.hpp │ ├── SyncWait.hpp │ ├── Task.hpp │ ├── WhenAllSequence.hpp │ └── WnenAllReady.hpp ├── Images ├── 1.svg ├── 2.svg ├── Design │ ├── DesignThreePanels.psd │ ├── PlayerIcons.ttf │ ├── a_LCDNova.ttf │ └── v1.1_Render │ │ ├── HealthPage.jpg │ │ ├── HealthPageLight.jpg │ │ ├── HealthPageLightCombined.jpg │ │ ├── MainPage.jpg │ │ ├── MainPageLight.jpg │ │ ├── MusicPage.jpg │ │ └── MusicPageLight.jpg ├── Player_1.svg ├── Player_2.svg ├── Player_redraw.svg ├── arrow.svg ├── arrow_right.svg ├── c4d8c1d4-cd41-4379-84de-3c51f9422b52.jfif ├── d303f735-42f6-4d54-85bb-543812d70a62.jfif ├── line.svg ├── photo_2019-11-03_00-30-05.jpg ├── photo_2019-11-03_00-31-58.jpg ├── photo_2020-01-09_01-23-37.jpg ├── player_next.svg ├── v1.1 │ ├── photo_2020-01-25_01-45-46.jpg │ ├── photo_2020-03-02_02-10-26.jpg │ ├── photo_2020-03-02_13-30-43.jpg │ ├── photo_2020-04-12_14-58-47.jpg │ ├── photo_2020-04-12_14-58-50.jpg │ └── photo_2020-04-12_14-58-51.jpg ├── v1.2 │ ├── WatchBorad-Bottom.jpg │ └── WatchBorad-top.jpg ├── Снимок.JPG └── Снимок1.JPG ├── LICENSE ├── README.md └── Schematic ├── Display_devboard_schematic.pdf ├── MissedParts.md └── WatchBorad ├── DCDC&PB.sch ├── DCDC_FG_BCH_PB.sch ├── MCU.sch ├── PSU_logic.sch ├── PowerSupply&PB.sch ├── PowerSupply.sch ├── PushButtons.sch ├── VibrationMotor.sch ├── WatchBoard.pdf ├── WatchBorad-cache.lib ├── WatchBorad.kicad_pcb ├── WatchBorad.kicad_pcb-bak ├── WatchBorad.pro ├── WatchBorad.sch ├── fp-lib-table └── sym-lib-table /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | *.slo 3 | *.lo 4 | *.o 5 | *.a 6 | *.la 7 | *.lai 8 | *.dll 9 | *.dylib 10 | 11 | .idea 12 | cmake-build-debug 13 | build-Firmware-ARM_Toolchain-Debug 14 | build-Firmware-Desktop_Qt_5_15_2_GCC_64bit-Debug 15 | test/build 16 | out 17 | 18 | #CMake gitignore 19 | CMakeLists.txt.user 20 | CMakeCache.txt 21 | CMakeFiles 22 | CMakeScripts 23 | Testing 24 | Makefile 25 | cmake_install.cmake 26 | install_manifest.txt 27 | compile_commands.json 28 | CTestTestfile.cmake 29 | _deps 30 | 31 | #Ignore build directory 32 | build/ 33 | out/ 34 | build_simulator/ 35 | 36 | #ignore vs temp folders 37 | .vs/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Firmware/3rdparty/fmt"] 2 | path = Firmware/3rdparty/fmt 3 | url = https://github.com/fmtlib/fmt.git 4 | [submodule "Firmware/3rdparty/etl"] 5 | path = Firmware/3rdparty/etl 6 | url = https://github.com/ETLCPP/etl.git 7 | [submodule "Firmware/graphics/lvgl_lib/lvgl_driver_backend/lv_drivers"] 8 | path = Firmware/graphics/lvgl_lib/lvgl_driver_backend/lv_drivers 9 | url = https://github.com/littlevgl/lv_drivers.git 10 | [submodule "Firmware/graphics/lvgl_lib/lvgl_library/lvgl"] 11 | path = Firmware/graphics/lvgl_lib/lvgl_library/lvgl 12 | url = https://github.com/lvgl/lvgl.git 13 | [submodule "Firmware/3rdparty/cppcoro_lib/cppcoro"] 14 | path = Firmware/3rdparty/cppcoro_lib/cppcoro 15 | url = https://github.com/lewissbaker/cppcoro.git 16 | [submodule "Schematic/KiCadLibrariesRoot"] 17 | path = Schematic/KiCadLibrariesRoot 18 | url = https://github.com/ValentiWorkLearning/KiCadLibrariesRoot.git 19 | -------------------------------------------------------------------------------- /Docs/DisplayGC9A01NormalTrace.pvs: -------------------------------------------------------------------------------- 1 | [D4] 2 | name=D4 3 | enabled=true 4 | color=4293776384 5 | conversion_type=0 6 | conv_options=0 7 | 8 | [decode_signal0] 9 | name=SPI 10 | enabled=true 11 | color=4281510450 12 | conversion_type=0 13 | conv_options=0 14 | decoders=1 15 | decoder0\id=spi 16 | decoder0\options=0 17 | channels=4 18 | channel0\name=CLK 19 | channel0\initial_pin_state=2 20 | channel0\assigned_signal_name=D0 21 | channel1\name=MISO 22 | channel1\initial_pin_state=2 23 | channel2\name=MOSI 24 | channel2\initial_pin_state=2 25 | channel2\assigned_signal_name=D1 26 | channel3\name=CS# 27 | channel3\initial_pin_state=2 28 | 29 | [D0] 30 | name=D0 31 | enabled=true 32 | color=4279638298 33 | conversion_type=0 34 | conv_options=0 35 | 36 | [D5] 37 | name=D5 38 | enabled=true 39 | color=4285780502 40 | conversion_type=0 41 | conv_options=0 42 | 43 | [D1] 44 | name=D1 45 | enabled=true 46 | color=4287582722 47 | conversion_type=0 48 | conv_options=0 49 | 50 | [D7] 51 | name=D7 52 | enabled=true 53 | color=4285878395 54 | conversion_type=0 55 | conv_options=0 56 | 57 | [D6] 58 | name=D6 59 | enabled=true 60 | color=4281623972 61 | conversion_type=0 62 | conv_options=0 63 | 64 | [D2] 65 | name=D2 66 | enabled=true 67 | color=4291559424 68 | conversion_type=0 69 | conv_options=0 70 | 71 | [D3] 72 | name=D3 73 | enabled=true 74 | color=4294277376 75 | conversion_type=0 76 | conv_options=0 77 | 78 | [General] 79 | decode_signals=1 80 | views=1 81 | 82 | [view0] 83 | scale=2.0048577321447794e-7 84 | v_offset=-120 85 | splitter_state=@ByteArray(\0\0\0\xff\0\0\0\x1\0\0\0\x2\0\0\0?\0\0\x4\x38\x1\0\0\0\x1\x1\0\0\0\x1\0) 86 | segment_display_mode=1 87 | offset=22 serialization::archive 14 0 0 0 0 67938408 23872048 90910915 293848 66697102 13638814 -8 0 0 6 88 | D7\trace_height=26 89 | D6\trace_height=26 90 | D5\trace_height=26 91 | D4\trace_height=26 92 | D0\trace_height=26 93 | D1\trace_height=26 94 | D2\trace_height=26 95 | D3\trace_height=26 96 | -------------------------------------------------------------------------------- /Docs/DisplayGC9A01NormalTrace.sr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Docs/DisplayGC9A01NormalTrace.sr -------------------------------------------------------------------------------- /Firmware/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Microsoft 3 | AccessModifierOffset: "-4" 4 | AlignAfterOpenBracket: AlwaysBreak 5 | AllowAllConstructorInitializersOnNextLine: "true" 6 | AllowAllParametersOfDeclarationOnNextLine: "false" 7 | AlwaysBreakAfterDefinitionReturnType: None 8 | BinPackArguments: "false" 9 | BinPackParameters: "false" 10 | BreakBeforeBinaryOperators: None 11 | BreakConstructorInitializers: BeforeComma 12 | BreakInheritanceList: BeforeComma 13 | ColumnLimit: "100" 14 | CompactNamespaces: "true" 15 | ConstructorInitializerAllOnOneLineOrOnePerLine: "true" 16 | ConstructorInitializerIndentWidth: "4" 17 | ContinuationIndentWidth: "4" 18 | ExperimentalAutoDetectBinPacking: "false" 19 | Language: Cpp 20 | PointerAlignment: Left 21 | ReflowComments: "true" 22 | SpaceBeforeCtorInitializerColon: "true" 23 | -------------------------------------------------------------------------------- /Firmware/.vscode/.cortex-debug.peripherals.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /Firmware/.vscode/.cortex-debug.registers.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /Firmware/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "pca10040/s132", 5 | "includePath": [ 6 | "${workspaceFolder}/sdk_dependent/config", 7 | "${env:NRF5_SDK_PATH}/**" 8 | ], 9 | "defines": [ 10 | "BOARD_PCA10040", 11 | "CONFIG_GPIO_AS_PINRESET", 12 | "FLOAT_ABI_HARD", 13 | "NRF52", 14 | "NRF52832_XXAA", 15 | "NRF52_PAN_74", 16 | "NRF_SD_BLE_API_VERSION=6", 17 | "S112", 18 | "SOFTDEVICE_PRESENT", 19 | "SWI_DISABLE0", 20 | "USE_NRFSDK_SIMULATOR", 21 | "USE_HARDWARE_DISPLAY_BACKEND", 22 | "USE_BLE_SERVICES", 23 | "USE_DEVICE_SPECIFIC", 24 | "LoggerSegger" 25 | ], 26 | "cStandard": "c11", 27 | "cppStandard": "c++17", 28 | "intelliSenseMode": "clang-x64", 29 | "configurationProvider": "go2sh.cmake-integration", 30 | "compileCommands": "${workspaceFolder}/build/compile_commands.json" 31 | } 32 | ], 33 | "version": 4 34 | } -------------------------------------------------------------------------------- /Firmware/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "cortex-debug", 9 | "request": "launch", 10 | "name": "Debug J-Link", 11 | "cwd": "${workspaceRoot}", 12 | "executable": "${workspaceRoot}//build//theOpenWatch.out", 13 | "serverpath": "JLinkGDBServerCL.exe", 14 | "servertype": "jlink", 15 | "device": "NRF52", 16 | "armToolchainPath": "C://gcc_arm_none_eabi_10_2_q4//bin", 17 | "interface": "swd", 18 | "serialNumber": "", //if you have more than one J-Link probe add the serial number here 19 | "runToMain": true, 20 | "svdFile": "${workspaceRoot}//nrf52_headers//nrf52.svd", 21 | 22 | "swoConfig": { 23 | "source": "probe", 24 | "enabled": true, 25 | "swoFrequency": 0, 26 | "cpuFrequency": 0, 27 | "decoders": [ 28 | { 29 | "encoding": "utf8", 30 | "port": 0, 31 | "label": "SWO", 32 | "type": "console" 33 | } 34 | ] 35 | } 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /Firmware/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "configure", 8 | "type": "shell", 9 | "command":"cd build && \"cmake -G\"Unix Makefiles\" ..", 10 | "problemMatcher": [] 11 | }, 12 | { 13 | "label": "build", 14 | "type": "shell", 15 | "command": "${env:GNU_TOOLS}/make", 16 | "options": { 17 | "cwd": "${workspaceFolder}" 18 | }, 19 | "problemMatcher": [] 20 | }, 21 | { 22 | "label": "clean", 23 | "type": "shell", 24 | "command": "${env:GNU_TOOLS}/make clean", 25 | "options": { 26 | "cwd": "${workspaceFolder}" 27 | }, 28 | "problemMatcher": [] 29 | }, 30 | { 31 | "label": "flash", 32 | "type": "shell", 33 | "command": "${env:GNU_TOOLS}/make flash", 34 | "options": { 35 | "cwd": "${workspaceFolder}" 36 | }, 37 | "group": { 38 | "kind": "build", 39 | "isDefault": true 40 | }, 41 | "problemMatcher": [] 42 | }, 43 | { 44 | "label": "flash_softdevice", 45 | "type": "shell", 46 | "command": "${env:GNU_TOOLS}/make flash_softdevice", 47 | "options": { 48 | "cwd": "${workspaceFolder}" 49 | }, 50 | "problemMatcher": [] 51 | }, 52 | { 53 | "label": "sdk_config", 54 | "type": "shell", 55 | "command": "${env:GNU_TOOLS}/make sdk_config", 56 | "options": { 57 | "cwd": "${workspaceFolder}" 58 | }, 59 | "problemMatcher": [] 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /Firmware/3rdparty/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Thanks, Alexander-Degtyar/OpenGreenery https://github.com/Alexander-Degtyar/OpenGreenery/blob/master/lib/CMakeLists.txt 2 | 3 | set(CMAKE_C_STANDARD 11) 4 | if( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR" ) 5 | set(CMAKE_CXX_STANDARD 20) 6 | #add_subdirectory( cppcoro_lib ) 7 | elseif( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) 8 | set(CMAKE_CXX_STANDARD 17) 9 | endif() 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | set( 3RD_PARTY_DIR CACHE STRING ${CMAKE_CURRENT_SOURCE_DIR} )# Libs root path 13 | add_subdirectory( etl ) 14 | add_subdirectory( fmt ) 15 | 16 | target_compile_definitions(fmt 17 | PUBLIC 18 | FMT_STATIC_THOUSANDS_SEPARATOR=1 19 | FMT_USE_FLOAT=0 20 | FMT_USE_DOUBLE=0 21 | FMT_USE_LONG_DOUBLE=0 22 | FMT_REDUCE_INT_INSTANTIATIONS=0 23 | ) -------------------------------------------------------------------------------- /Firmware/3rdparty/cppcoro_lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(cppcoro LANGUAGES CXX) 3 | 4 | set(CMAKE_CXX_EXTENSIONS OFF) 5 | 6 | add_library(cppcoro) 7 | target_sources(cppcoro PRIVATE 8 | "cppcoro/lib/async_auto_reset_event.cpp" 9 | "cppcoro/lib/async_manual_reset_event.cpp" 10 | "cppcoro/lib/async_mutex.cpp" 11 | "cppcoro/lib/cancellation_state.cpp" 12 | "cppcoro/lib/cancellation_token.cpp" 13 | "cppcoro/lib/cancellation_source.cpp" 14 | "cppcoro/lib/cancellation_registration.cpp" 15 | "cppcoro/lib/lightweight_manual_reset_event.cpp" 16 | "cppcoro/lib/ip_address.cpp" 17 | "cppcoro/lib/ip_endpoint.cpp" 18 | "cppcoro/lib/ipv4_address.cpp" 19 | "cppcoro/lib/ipv4_endpoint.cpp" 20 | "cppcoro/lib/ipv6_address.cpp" 21 | "cppcoro/lib/ipv6_endpoint.cpp" 22 | "cppcoro/lib/static_thread_pool.cpp" 23 | "cppcoro/lib/auto_reset_event.cpp" 24 | "cppcoro/lib/spin_wait.cpp" 25 | "cppcoro/lib/spin_mutex.cpp") 26 | 27 | if(WIN32) 28 | target_sources(cppcoro PRIVATE 29 | "cppcoro/lib/win32.cpp" 30 | "cppcoro/lib/io_service.cpp" 31 | "cppcoro/lib/file.cpp" 32 | "cppcoro/lib/readable_file.cpp" 33 | "cppcoro/lib/writable_file.cpp" 34 | "cppcoro/lib/read_only_file.cpp" 35 | "cppcoro/lib/write_only_file.cpp" 36 | "cppcoro/lib/read_write_file.cpp" 37 | "cppcoro/lib/file_read_operation.cpp" 38 | "cppcoro/lib/file_write_operation.cpp" 39 | "cppcoro/lib/socket_helpers.cpp" 40 | "cppcoro/lib/socket.cpp" 41 | "cppcoro/lib/socket_accept_operation.cpp" 42 | "cppcoro/lib/socket_connect_operation.cpp" 43 | "cppcoro/lib/socket_disconnect_operation.cpp" 44 | "cppcoro/lib/socket_send_operation.cpp" 45 | "cppcoro/lib/socket_send_to_operation.cpp" 46 | "cppcoro/lib/socket_recv_operation.cpp" 47 | "cppcoro/lib/socket_recv_from_operation.cpp") 48 | endif() 49 | 50 | target_include_directories(cppcoro PUBLIC 51 | $ 52 | ) 53 | target_compile_definitions(cppcoro PRIVATE _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING ) 54 | target_compile_features(cppcoro PUBLIC cxx_std_20) -------------------------------------------------------------------------------- /Firmware/CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "x64-Debug", 5 | "generator": "Ninja", 6 | "configurationType": "Debug", 7 | "inheritEnvironments": [ "msvc_x64_x64" ], 8 | "buildRoot": "${projectDir}\\build_simulator\\${name}", 9 | "installRoot": "${projectDir}\\build_simulator\\${name}", 10 | "cmakeCommandArgs": "-DTARGET_PLATFORM:STRING=\"FIRMWARE_SIMULATOR\" -DPACKAGE_TESTS=ON -DENABLE_SANITIZE_BUILD=ON", 11 | "buildCommandArgs": "", 12 | "addressSanitizerEnabled": true, 13 | "ctestCommandArgs": "" 14 | }, 15 | { 16 | "name": "x86-Debug", 17 | "generator": "Ninja", 18 | "configurationType": "Debug", 19 | "buildRoot": "${projectDir}\\out\\buildx86\\${name}", 20 | "installRoot": "${projectDir}\\out\\install\\${name}", 21 | "cmakeCommandArgs": "-DTARGET_PLATFORM:STRING=\"FIRMWARE_SIMULATOR\" -DPACKAGE_TESTS=ON", 22 | "buildCommandArgs": "", 23 | "ctestCommandArgs": "", 24 | "addressSanitizerEnabled": false, 25 | "inheritEnvironments": [ "msvc_x86" ] 26 | }, 27 | { 28 | "name": "x86-Release", 29 | "generator": "Ninja", 30 | "configurationType": "Release", 31 | "buildRoot": "${projectDir}\\out\\buildx86_release\\${name}", 32 | "installRoot": "${projectDir}\\out\\install\\${name}", 33 | "cmakeCommandArgs": "-DTARGET_PLATFORM:STRING=\"FIRMWARE_SIMULATOR\" -DPACKAGE_TESTS=ON", 34 | "buildCommandArgs": "", 35 | "ctestCommandArgs": "", 36 | "addressSanitizerEnabled": false, 37 | "inheritEnvironments": [ "msvc_x86" ] 38 | }, 39 | { 40 | "name": "ArmToolchainBareMetalDebug", 41 | "cacheRoot": "${projectDir}\\build_firmware", 42 | "inheritEnvironments": [], 43 | "cacheGenerationCommand": "cmake -G\"Ninja\" -DTARGET_PLATFORM:STRING=\"ARM_CORTEX\" -DEXECUTE_MCU_FLASHING=OFF -DCMAKE_BUILD_TYPE:STRING=Debug -DREDUCE_LVGL_BINARY_SIZE=ON -DPACKAGE_TESTS=OFF -S${projectDir}\\ -B${projectDir}\\build_firmware" 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /Firmware/ap_application.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "graphics/gs_lvgl_service.hpp" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include 16 | 17 | #include 18 | 19 | class Application : public Utils::noncopyable 20 | { 21 | 22 | public: 23 | Application() noexcept; 24 | 25 | ~Application() noexcept; 26 | 27 | public: 28 | void runApplicationLoop() noexcept; 29 | 30 | private: 31 | void initBoard() noexcept; 32 | 33 | void initServices() noexcept; 34 | 35 | void initPeripheral() noexcept; 36 | 37 | void initBleStack() noexcept; 38 | 39 | void initGraphicsStack() noexcept; 40 | 41 | void connectBoardSpecificEvents() noexcept; 42 | 43 | private: 44 | Ble::Stack::TSoftDevicePtr m_bleStackKeeper; 45 | std::unique_ptr m_fakeServiceProvider; 46 | std::unique_ptr 47 | m_batteryLevelService; 48 | std::unique_ptr m_heartrateService; 49 | std::unique_ptr m_dateTimeService; 50 | std::unique_ptr m_graphicsService; 51 | WatchBoard::TBoardPtr m_pBoardImpl; 52 | }; 53 | -------------------------------------------------------------------------------- /Firmware/cmake/arm-gcc-toolchain.cmake: -------------------------------------------------------------------------------- 1 | get_filename_component(NORDIC_CMAKE_DIR ${CMAKE_CURRENT_LIST_FILE} DIRECTORY) 2 | list(APPEND CMAKE_MODULE_PATH ${NORDIC_CMAKE_DIR}) 3 | 4 | include(base) 5 | 6 | set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) 7 | find_program(CMAKE_C_COMPILER NAMES ${NORDIC_TARGET_TRIPLET}-gcc PATHS ${TOOLCHAIN_BIN_PATH} ) 8 | find_program(CMAKE_CXX_COMPILER NAMES ${NORDIC_TARGET_TRIPLET}-c++ PATHS ${TOOLCHAIN_BIN_PATH} ) 9 | find_program(CMAKE_ASM_COMPILER NAMES ${NORDIC_TARGET_TRIPLET} PATHS ${TOOLCHAIN_BIN_PATH} ) 10 | find_program(CMAKE_SIZE_UTIL NAMES ${NORDIC_TARGET_TRIPLET}-size PATHS ${TOOLCHAIN_BIN_PATH} ) 11 | find_program(CMAKE_OBJCOPY NAMES ${NORDIC_TARGET_TRIPLET}-objcopy PATHS ${TOOLCHAIN_BIN_PATH} ) -------------------------------------------------------------------------------- /Firmware/cmake/base.cmake: -------------------------------------------------------------------------------- 1 | if( NOT ARM_NONE_EABI_TOOLCHAIN_PATH ) 2 | #set(ARM_NONE_EABI_TOOLCHAIN_PATH "C:/gcc_none_eabi_9_2_1") 3 | set(ARM_NONE_EABI_TOOLCHAIN_PATH "C:/gcc_arm_none_eabi_10_3") 4 | message(STATUS "No ARM_NONE_EABI_TOOLCHAIN_PATH specified, using default: " ${ARM_NONE_EABI_TOOLCHAIN_PATH}) 5 | else() 6 | message(STATUS " ARM_NONE_EABI_TOOLCHAIN_PATH specified using: " ${ARM_NONE_EABI_TOOLCHAIN_PATH}) 7 | file(TO_CMAKE_PATH "${ARM_NONE_EABI_TOOLCHAIN_PATH}" ARM_NONE_EABI_TOOLCHAIN_PATH) 8 | endif() 9 | 10 | if(NOT NORDIC_TARGET_TRIPLET) 11 | set(NORDIC_TARGET_TRIPLET "arm-none-eabi") 12 | message(STATUS "No NORDIC_TARGET_TRIPLET specified, using default: " ${NORDIC_TARGET_TRIPLET}) 13 | endif() 14 | 15 | set(CMAKE_SYSTEM_NAME Generic) 16 | set(CMAKE_SYSTEM_PROCESSOR arm) 17 | 18 | set(TOOLCHAIN_SYSROOT "${ARM_NONE_EABI_TOOLCHAIN_PATH}/${NORDIC_TARGET_TRIPLET}") 19 | set(TOOLCHAIN_BIN_PATH "${ARM_NONE_EABI_TOOLCHAIN_PATH}/bin") 20 | set(TOOLCHAIN_INC_PATH "${ARM_NONE_EABI_TOOLCHAIN_PATH}/${NORDIC_TARGET_TRIPLET}/include") 21 | set(TOOLCHAIN_LIB_PATH "${ARM_NONE_EABI_TOOLCHAIN_PATH}/${NORDIC_TARGET_TRIPLET}/lib") 22 | 23 | 24 | find_program(CMAKE_OBJCOPY NAMES ${NORDIC_TARGET_TRIPLET}-objcopy PATHS ${TOOLCHAIN_BIN_PATH}) 25 | find_program(CMAKE_OBJDUMP NAMES ${NORDIC_TARGET_TRIPLET}-objdump PATHS ${TOOLCHAIN_BIN_PATH}) 26 | find_program(CMAKE_SIZE NAMES ${NORDIC_TARGET_TRIPLET}-size PATHS ${TOOLCHAIN_BIN_PATH}) 27 | find_program(CMAKE_DEBUGGER NAMES ${NORDIC_TARGET_TRIPLET}-gdb PATHS ${TOOLCHAIN_BIN_PATH}) 28 | find_program(CMAKE_CPPFILT NAMES ${NORDIC_TARGET_TRIPLET}-c++filt PATHS ${TOOLCHAIN_BIN_PATH}) 29 | 30 | 31 | #include(nordic/sdk) 32 | -------------------------------------------------------------------------------- /Firmware/cmake/jlink/commandline.txt: -------------------------------------------------------------------------------- 1 | nrfjprog.exe --program C:/Nordic/components/softdevice/s132/hex/s132_nrf52_6.1.1_softdevice.hex -f nrf52 --sectorerase 2 | 3 | 4 | nrfjprog.exe --program .\BlinkyExample.hex -f nrf52 5 | 6 | nrfjprog.exe -e -f nrf52 7 | 8 | nrfjprog.exe --reset -f nrf52 9 | -------------------------------------------------------------------------------- /Firmware/cmake/jlink/runJLinkExe-nrf52: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | JLinkExe -device nrf52 -if swd -speed 4000 -autoconnect 1 -------------------------------------------------------------------------------- /Firmware/cmake/jlink/runJLinkGDBServer-nrf52: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | JLinkGDBServer -device nrf52 -strict -timeout 0 -nogui -if swd -speed 1000 -endian little -------------------------------------------------------------------------------- /Firmware/cmake/jlink/runJLinkRTTClient: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | JLinkRTTClient -------------------------------------------------------------------------------- /Firmware/cmake/nordic/nordic_postbuild.cmake: -------------------------------------------------------------------------------- 1 | 2 | if(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin") 3 | set(TERMINAL "open") 4 | elseif(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows") 5 | set(TERMINAL "sh") 6 | else() 7 | set(TERMINAL "gnome-terminal") 8 | endif() 9 | 10 | function (nordicSdk_createBinAndHexFiles TARGET_NAME) 11 | add_custom_command(TARGET ${TARGET_NAME} 12 | POST_BUILD 13 | COMMAND ${CMAKE_SIZE_UTIL} ${TARGET_NAME}.out 14 | COMMAND ${CMAKE_OBJCOPY} -O binary ${TARGET_NAME}.out "${TARGET_NAME}.bin" 15 | COMMAND ${CMAKE_OBJCOPY} -O ihex ${TARGET_NAME}.out "${TARGET_NAME}.hex" 16 | COMMENT "post build steps for ${TARGET_NAME}" 17 | ) 18 | endfunction() 19 | 20 | 21 | find_program( NRFJPROG NAMES nrfjprog PATHS $ENV{NRFJPROG_PATH} ) 22 | 23 | function( nordicSdk_flashSoftDevice SOFTDEVICE_PATH NRF_TARGET ) 24 | add_custom_target(FLASH_ERASE ALL 25 | COMMAND sleep 0.5s 26 | COMMAND ${NRFJPROG} --eraseall -f ${NRF_TARGET} 27 | COMMENT "erasing flashing" 28 | ) 29 | add_custom_target(FLASH_SOFTDEVICE ALL 30 | DEPENDS ${EXECUTABLE_NAME} FLASH_ERASE 31 | COMMAND ${NRFJPROG} --program ${SOFTDEVICE_PATH} -f ${NRF_TARGET} --sectorerase 32 | COMMAND sleep 1s 33 | COMMAND ${NRFJPROG} --reset -f ${NRF_TARGET} 34 | COMMENT "flashing SoftDevice" 35 | ) 36 | endfunction(nordicSdk_flashSoftDevice) 37 | 38 | function(nordicSdk_flashFirmware EXECUTABLE_NAME ) 39 | add_custom_target("FLASH_${EXECUTABLE_NAME}" ALL 40 | DEPENDS ${EXECUTABLE_NAME} FLASH_ERASE FLASH_SOFTDEVICE 41 | COMMAND ${NRFJPROG} --program ${EXECUTABLE_NAME}.hex -f ${NRF_TARGET} --sectorerase 42 | COMMAND sleep 0.5s 43 | COMMAND ${NRFJPROG} --reset -f ${NRF_TARGET} 44 | COMMENT "flashing ${EXECUTABLE_NAME}.hex" 45 | ) 46 | endfunction(nordicSdk_flashFirmware ) 47 | -------------------------------------------------------------------------------- /Firmware/conanfile.txt: -------------------------------------------------------------------------------- 1 | [requires] 2 | sdl/2.0.16 3 | 4 | [options] 5 | sdl:shared=True 6 | 7 | [imports] 8 | bin, *.dll -> bin 9 | 10 | [generators] 11 | cmake 12 | -------------------------------------------------------------------------------- /Firmware/drivers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set ( DRIVER_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}) 2 | 3 | 4 | add_subdirectory(headers) 5 | add_subdirectory(platform_delay) 6 | add_subdirectory(factory_impl) 7 | add_subdirectory(i2c) 8 | add_subdirectory(board) 9 | add_subdirectory(gpio) 10 | add_subdirectory(ble) 11 | add_subdirectory(buttons) 12 | 13 | add_subdirectory(display) 14 | add_subdirectory(spi) 15 | add_subdirectory(winbondflash) 16 | -------------------------------------------------------------------------------- /Firmware/drivers/ble/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set (TARGET_NAME ble_services ) 2 | 3 | add_library( 4 | ${TARGET_NAME} 5 | ${TARGET_SOURCE} 6 | ) 7 | 8 | target_link_libraries( 9 | ${TARGET_NAME} 10 | PUBLIC 11 | UtilsLibrary 12 | etl 13 | drivers_ih 14 | logger_service 15 | ) 16 | 17 | if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) 18 | 19 | target_include_directories( 20 | ${TARGET_NAME} 21 | PUBLIC 22 | ${CMAKE_CURRENT_LIST_DIR}/inc 23 | PRIVATE 24 | ${CMAKE_CURRENT_LIST_DIR}/inc 25 | ) 26 | 27 | target_link_libraries( 28 | ${TARGET_NAME} 29 | PUBLIC 30 | NordicSDK::Ble 31 | NordicSDK::App::FDS 32 | NordicSDK::Bsp::BleButton 33 | NordicSDK::Ble::PeerManager 34 | NordicSDK::Ble::ServiceDbDiscovery 35 | NordicSDK::BleService::BateryService 36 | NordicSDK::BleService::CurrentTimeClient 37 | NordicSDK::BleService::HeartrateService 38 | ) 39 | 40 | target_sources( 41 | ${TARGET_NAME} 42 | PRIVATE 43 | ble_custom_service.cpp 44 | ble_softdevice.cpp 45 | ble_battery_service.cpp 46 | ble_datetime_service.cpp 47 | ble_heartrate_service.cpp 48 | ) 49 | elseif( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR" ) 50 | target_include_directories( 51 | ${TARGET_NAME} 52 | PUBLIC 53 | ${CMAKE_CURRENT_LIST_DIR}/inc/ble 54 | ) 55 | target_sources( 56 | ${TARGET_NAME} 57 | PRIVATE 58 | ble_desktop_softdevice.cpp 59 | ) 60 | 61 | endif() 62 | 63 | target_link_options( 64 | ${TARGET_NAME} 65 | PUBLIC 66 | ${CPU_FLAGS} 67 | ) 68 | 69 | target_compile_options( 70 | ${TARGET_NAME} 71 | PUBLIC 72 | -Os 73 | ${COMMON_FLAGS} 74 | ) 75 | -------------------------------------------------------------------------------- /Firmware/drivers/ble/ble_battery_service.cpp: -------------------------------------------------------------------------------- 1 | #include "inc/ble/nordic_ble/ble_battery_service.hpp" 2 | 3 | #include "ble.h" 4 | #include "ble_bas.h" 5 | #include "ble_srv_common.h" 6 | 7 | #include "app_error.h" 8 | 9 | namespace Ble::BatteryService 10 | { 11 | 12 | inline constexpr std::uint16_t ServiceUuid = BLE_UUID_BATTERY_SERVICE; 13 | inline constexpr std::uint8_t ServiceType = BLE_UUID_TYPE_BLE; 14 | 15 | BLE_BAS_DEF(m_bas); 16 | 17 | BatteryLevelService::BatteryLevelService() noexcept 18 | { 19 | initService(); 20 | } 21 | 22 | void BatteryLevelService::initService() noexcept 23 | { 24 | ret_code_t errorCode{}; 25 | ble_bas_init_t batteryServiceInit{}; 26 | 27 | batteryServiceInit.bl_rd_sec = SEC_OPEN; 28 | batteryServiceInit.bl_cccd_wr_sec = SEC_OPEN; 29 | batteryServiceInit.bl_report_rd_sec = SEC_OPEN; 30 | 31 | batteryServiceInit.evt_handler = nullptr; 32 | batteryServiceInit.support_notification = true; 33 | batteryServiceInit.p_report_ref = nullptr; 34 | batteryServiceInit.initial_batt_level = InitialBatteryLevel; 35 | 36 | errorCode = ble_bas_init(&m_bas, &batteryServiceInit); 37 | APP_ERROR_CHECK(errorCode); 38 | } 39 | 40 | void BatteryLevelService::onBatteryLevelChanged(std::uint8_t _newBatteryLevel) noexcept 41 | { 42 | ret_code_t errorCode{}; 43 | std::uint8_t levelToSet{_newBatteryLevel}; 44 | 45 | errorCode = ble_bas_battery_level_update(&m_bas, levelToSet, BLE_CONN_HANDLE_ALL); 46 | 47 | if ((errorCode != NRF_SUCCESS) && (errorCode != NRF_ERROR_INVALID_STATE) && 48 | (errorCode != NRF_ERROR_RESOURCES) && (errorCode != NRF_ERROR_BUSY) && 49 | (errorCode != BLE_ERROR_GATTS_SYS_ATTR_MISSING)) 50 | { 51 | APP_ERROR_CHECK(errorCode); 52 | } 53 | } 54 | 55 | } // namespace Ble::BatteryService -------------------------------------------------------------------------------- /Firmware/drivers/ble/ble_desktop_softdevice.cpp: -------------------------------------------------------------------------------- 1 | #include "desktop_ble/ble_desktop_softdevice.hpp" 2 | 3 | #include 4 | 5 | namespace Ble::Stack 6 | { 7 | 8 | DesktopBleStackKeeper::DesktopBleStackKeeper(ServiceFactory::TBleFactoryPtr&& _pServiceCreator) 9 | : m_isConnected{false}, m_pServiceCreator{std::move(_pServiceCreator)} 10 | { 11 | m_batteryService = m_pServiceCreator->getBatteryService(); 12 | m_dateTimeService = m_pServiceCreator->getDateTimeService(std::nullopt); 13 | } 14 | 15 | Ble::BatteryService::IBatteryLevelService& DesktopBleStackKeeper::getBatteryService() noexcept 16 | { 17 | return *m_batteryService.get(); 18 | } 19 | 20 | const Ble::BatteryService::IBatteryLevelService& DesktopBleStackKeeper::getBatteryService() 21 | const noexcept 22 | { 23 | return *m_batteryService.get(); 24 | } 25 | 26 | Ble::DateTimeService::IDateTimeService& DesktopBleStackKeeper::getDateTimeService() noexcept 27 | { 28 | return *m_dateTimeService.get(); 29 | } 30 | 31 | const Ble::DateTimeService::IDateTimeService& DesktopBleStackKeeper::getDateTimeService() 32 | const noexcept 33 | { 34 | return *m_dateTimeService.get(); 35 | } 36 | 37 | std::unique_ptr createBleStackKeeper( 38 | ServiceFactory::TBleFactoryPtr&& _pServiceCreator) noexcept 39 | { 40 | return std::make_unique(std::move(_pServiceCreator)); 41 | } 42 | 43 | } // namespace Ble::Stack -------------------------------------------------------------------------------- /Firmware/drivers/ble/ble_heartrate_service.cpp: -------------------------------------------------------------------------------- 1 | #include "inc/ble/nordic_ble/ble_heartrate_service.hpp" 2 | 3 | #include "app_error.h" 4 | #include "ble_hrs.h" 5 | 6 | #include 7 | 8 | namespace 9 | { 10 | BLE_HRS_DEF(m_hrs); 11 | } 12 | 13 | namespace Ble::HeartrateService 14 | { 15 | 16 | NordicHeartrateService::NordicHeartrateService() noexcept 17 | { 18 | initService(); 19 | } 20 | 21 | NordicHeartrateService::~NordicHeartrateService() = default; 22 | 23 | void NordicHeartrateService::onHeartrateChanged(std::uint8_t _newHeartrateLevel) noexcept 24 | { 25 | ret_code_t errorCode = ble_hrs_heart_rate_measurement_send(&m_hrs, _newHeartrateLevel); 26 | APP_ERROR_CHECK(errorCode); 27 | } 28 | 29 | void NordicHeartrateService::initService() noexcept 30 | { 31 | ble_hrs_init_t heartrateDescriptor{}; 32 | std::uint8_t bodySensorLocation = BLE_HRS_BODY_SENSOR_LOCATION_FINGER; 33 | 34 | heartrateDescriptor.evt_handler = nullptr; 35 | heartrateDescriptor.is_sensor_contact_supported = true; 36 | heartrateDescriptor.p_body_sensor_location = &bodySensorLocation; 37 | 38 | heartrateDescriptor.hrm_cccd_wr_sec = SEC_OPEN; 39 | heartrateDescriptor.bsl_rd_sec = SEC_OPEN; 40 | 41 | ret_code_t errorCode = ble_hrs_init(&m_hrs, &heartrateDescriptor); 42 | APP_ERROR_CHECK(errorCode); 43 | } 44 | 45 | void NordicHeartrateService::setSensorLocation( 46 | Ble::HeartrateService::SensorLocation _sensorLocation) noexcept 47 | { 48 | // TODO me? 49 | } 50 | 51 | } // namespace Ble::HeartrateService 52 | -------------------------------------------------------------------------------- /Firmware/drivers/ble/inc/ble/desktop_ble/ble_desktop_softdevice.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/drivers/ih_ble_battery_service.hpp" 4 | #include "ih/drivers/ih_ble_dts_service.hpp" 5 | #include "ih/drivers/ih_ble_service_factory.hpp" 6 | #include "ih/drivers/ih_ible_softdevice.hpp" 7 | 8 | #include 9 | 10 | namespace Ble::Stack 11 | { 12 | class DesktopBleStackKeeper : public IBleSoftDevice 13 | { 14 | 15 | public: 16 | DesktopBleStackKeeper(ServiceFactory::TBleFactoryPtr&& _pServiceCreator); 17 | ~DesktopBleStackKeeper() override = default; 18 | 19 | public: 20 | Ble::BatteryService::IBatteryLevelService& getBatteryService() noexcept override; 21 | 22 | const Ble::BatteryService::IBatteryLevelService& getBatteryService() const noexcept override; 23 | 24 | Ble::DateTimeService::IDateTimeService& getDateTimeService() noexcept override; 25 | 26 | const Ble::DateTimeService::IDateTimeService& getDateTimeService() const noexcept override; 27 | 28 | private: 29 | std::atomic m_isConnected; 30 | 31 | ServiceFactory::TBleFactoryPtr m_pServiceCreator; 32 | Ble::ServiceFactory::IBleServiceFactory::TBatteryServicePtr m_batteryService; 33 | Ble::ServiceFactory::IBleServiceFactory::TDateTimeServicePtr m_dateTimeService; 34 | }; 35 | 36 | std::unique_ptr createBleStackKeeper( 37 | ServiceFactory::TBleFactoryPtr&& _pServiceCreator) noexcept; 38 | 39 | } // namespace Ble::Stack -------------------------------------------------------------------------------- /Firmware/drivers/ble/inc/ble/nordic_ble/ble_battery_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/drivers/ih_ble_battery_service.hpp" 4 | 5 | #include "utils/Noncopyable.hpp" 6 | #include "utils/SimpleSignal.hpp" 7 | 8 | #include 9 | 10 | namespace Ble::BatteryService 11 | { 12 | inline constexpr std::uint8_t InitialBatteryLevel = 100; 13 | 14 | class BatteryLevelService : public IBatteryLevelService 15 | { 16 | 17 | public: 18 | BatteryLevelService() noexcept; 19 | ~BatteryLevelService() override = default; 20 | 21 | public: 22 | void onBatteryLevelChanged(std::uint8_t _newBatteryLevel) noexcept override; 23 | 24 | private: 25 | void initService() noexcept; 26 | }; 27 | 28 | } // namespace Ble::BatteryService -------------------------------------------------------------------------------- /Firmware/drivers/ble/inc/ble/nordic_ble/ble_custom_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ble.h" 4 | #include "ble_srv_common.h" 5 | 6 | #include "utils/MetaUtils.hpp" 7 | #include "utils/Noncopyable.hpp" 8 | 9 | #include 10 | #include 11 | 12 | namespace Ble::CustomService 13 | { 14 | inline constexpr std::uint8_t UuidSize = 16; 15 | 16 | inline constexpr std::array UuidBase_BE = { 17 | 0xF3, 18 | 0x64, 19 | 0xAD, 20 | 0xC9, 21 | 0xB0, 22 | 0x00, 23 | 0x40, 24 | 0x42, 25 | 0xBA, 26 | 0x50, 27 | 0x05, 28 | 0xCA, 29 | 0x45, 30 | 0xBF, 31 | 0x8A, 32 | 0xBC}; 33 | 34 | inline constexpr std::array CustomServiceUuid{Meta::reverseArray(UuidBase_BE)}; 35 | inline constexpr std::uint8_t ServiceType = BLE_UUID_TYPE_VENDOR_BEGIN; 36 | 37 | inline constexpr std::uint16_t ServiceUuid = 0x1400; 38 | inline constexpr std::uint16_t ValueCharUuid = 0x1401; 39 | 40 | inline constexpr std::uint8_t ServiceObserverPriority = 2; 41 | 42 | class CustomService : private Utils::noncopyable 43 | { 44 | 45 | public: 46 | CustomService(); 47 | ~CustomService() = default; 48 | 49 | private: 50 | void initCustomService() noexcept; 51 | 52 | private: 53 | using TThis = CustomService; 54 | 55 | static void serviceBleEventHandler(ble_evt_t const* _pEvent, void* _pContext) noexcept; 56 | 57 | void bleEventHandler(ble_evt_t const* _pEvent) noexcept; 58 | 59 | void initEventHandler() noexcept; 60 | 61 | private: 62 | std::uint16_t m_serviceHandle; 63 | ble_gatts_char_handles_t m_customValueHandles; 64 | std::uint16_t m_connectionHandle; 65 | std::uint8_t m_uuidType; 66 | }; 67 | } // namespace Ble::CustomService 68 | -------------------------------------------------------------------------------- /Firmware/drivers/ble/inc/ble/nordic_ble/ble_datetime_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/drivers/ih_ble_dts_service.hpp" 4 | 5 | #include "ble_cts_c.h" 6 | 7 | namespace Ble::DateTimeService 8 | { 9 | 10 | class DateTimeServiceNordic : public IDateTimeService 11 | { 12 | 13 | public: 14 | DateTimeServiceNordic(const std::any& _pGattQueue); 15 | 16 | public: 17 | virtual ~DateTimeServiceNordic() override = default; 18 | 19 | public: 20 | void handleDiscoveryEvent(const std::any& _pBleDbDiscoveryEvent) noexcept override; 21 | 22 | private: 23 | void initService(const std::any& _pGattQueue) noexcept; 24 | 25 | void serviceEventHandler(ble_cts_c_t* _pCurrentTimeService, ble_cts_c_evt_t* _pEvent) noexcept; 26 | }; 27 | 28 | } // namespace Ble::DateTimeService -------------------------------------------------------------------------------- /Firmware/drivers/ble/inc/ble/nordic_ble/ble_heartrate_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/drivers/ih_ble_heartrate_service.hpp" 4 | 5 | namespace Ble::HeartrateService 6 | { 7 | 8 | class NordicHeartrateService : public IHeartrateService 9 | { 10 | 11 | public: 12 | NordicHeartrateService() noexcept; 13 | ~NordicHeartrateService() override; 14 | 15 | public: 16 | void onHeartrateChanged(std::uint8_t _newHeartrateLevel) noexcept override; 17 | 18 | void setSensorLocation(SensorLocation _sensorLocation) noexcept override; 19 | 20 | private: 21 | void initService() noexcept; 22 | }; 23 | 24 | } // namespace Ble::HeartrateService 25 | -------------------------------------------------------------------------------- /Firmware/drivers/board/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | watchboard 3 | watchboard.cpp 4 | inc/board/hardware_usings.hpp 5 | ) 6 | 7 | target_include_directories( 8 | watchboard 9 | PUBLIC 10 | ${CMAKE_CURRENT_LIST_DIR}/inc 11 | ) 12 | 13 | target_link_libraries( 14 | watchboard 15 | PUBLIC 16 | buttons_driver 17 | spi 18 | windbond_spi_flash_driver 19 | PRIVATE 20 | UtilsLibrary 21 | logger_service 22 | platform_delay 23 | drivers_ih 24 | fmt 25 | ) 26 | 27 | target_compile_features( 28 | watchboard 29 | PRIVATE 30 | cxx_std_20 31 | ) 32 | 33 | if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) 34 | target_link_libraries(watchboard 35 | PRIVATE 36 | NordicSDK::Common 37 | NordicSDK::Bsp 38 | NordicSDK::App::Timer 39 | ) 40 | 41 | target_compile_definitions( 42 | watchboard 43 | PUBLIC 44 | USE_DEVICE_SPECIFIC 45 | ) 46 | elseif( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR") 47 | endif() 48 | 49 | 50 | target_compile_options( 51 | watchboard 52 | PRIVATE 53 | $<$:-fcoroutines> 54 | ) 55 | -------------------------------------------------------------------------------- /Firmware/drivers/board/inc/board/hardware_usings.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #ifndef USE_DEVICE_SPECIFIC 5 | #include 6 | #include 7 | #else 8 | #include 9 | #include 10 | #endif 11 | 12 | #include 13 | #include 14 | 15 | namespace Hal 16 | { 17 | 18 | #ifndef USE_DEVICE_SPECIFIC 19 | using BoardTimer = Buttons::FirmwareSimulatorTimerBackend; 20 | using ButtonsBackend = Buttons::FirmwareSimulatorButtonsBackend; 21 | using TFlashDriver = ExternalFlash::WinbondFlashDriver< 22 | Interface::SpiTemplated::SpiBus>; 23 | #else 24 | using BoardTimer = Buttons::NordicTimerBackend; 25 | using ButtonsBackend = Buttons::NordicButtonsBackend; 26 | using TFlashDriver = ExternalFlash::WinbondFlashDriver< 27 | Interface::SpiTemplated::SpiBus>>; 30 | #endif 31 | 32 | using ButtonsDriver = Buttons::ButtonsDriverTemplate; 33 | }; // namespace Hal 34 | -------------------------------------------------------------------------------- /Firmware/drivers/board/inc/board/watchboard.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils/FastPimpl.hpp" 4 | #include "utils/Platform.hpp" 5 | 6 | #include "hardware_usings.hpp" 7 | #include 8 | 9 | namespace WatchBoard 10 | { 11 | 12 | class Board : private Utils::noncopyable 13 | { 14 | 15 | public: 16 | Board() noexcept; 17 | 18 | public: 19 | void ledToggle() noexcept; 20 | 21 | Hal::ButtonsDriver* getButtonsDriver() noexcept; 22 | 23 | private: 24 | void initBoard() noexcept; 25 | 26 | void initBoardTimer() noexcept; 27 | 28 | void initBoardSpiFlash() noexcept; 29 | 30 | std::uint32_t convertToTimerTicks(std::chrono::milliseconds _interval) noexcept; 31 | 32 | private: 33 | const std::chrono::milliseconds LedToggleTimeout = std::chrono::milliseconds{300}; 34 | 35 | private: 36 | Hal::ButtonsDriver m_buttonsDriver; 37 | using TFlashDriverPtr = std::unique_ptr; 38 | 39 | TFlashDriverPtr m_pFlashDriver; 40 | }; 41 | 42 | using TBoardPtr = std::unique_ptr; 43 | TBoardPtr createBoard() noexcept; 44 | 45 | } // namespace WatchBoard 46 | -------------------------------------------------------------------------------- /Firmware/drivers/buttons/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | buttons_driver 3 | 4 | 5 | ) 6 | 7 | target_include_directories( 8 | buttons_driver 9 | PUBLIC 10 | ${CMAKE_CURRENT_LIST_DIR}/inc 11 | ) 12 | 13 | target_link_libraries( 14 | buttons_driver 15 | PRIVATE 16 | UtilsLibrary 17 | logger_service 18 | platform_delay 19 | drivers_ih 20 | ) 21 | 22 | if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) 23 | target_sources( 24 | buttons_driver 25 | PRIVATE 26 | bt_nordic_hardware_backend.cpp 27 | ) 28 | target_link_libraries(buttons_driver 29 | PRIVATE 30 | NordicSDK::Common 31 | NordicSDK::Bsp 32 | NordicSDK::App::Timer 33 | ) 34 | 35 | target_compile_definitions( 36 | buttons_driver 37 | PRIVATE 38 | USE_DEVICE_SPECIFIC 39 | ) 40 | elseif( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR" ) 41 | string(TOLOWER ${TARGET_PLATFORM} BUTTONS_PLATFORM_NAME) 42 | target_sources( 43 | buttons_driver 44 | PRIVATE 45 | bt_${BUTTONS_PLATFORM_NAME}_hardware_buttons_manual_backend.cpp 46 | ) 47 | endif() 48 | -------------------------------------------------------------------------------- /Firmware/drivers/buttons/bt_os_stub_hardware_buttons_backend.cpp: -------------------------------------------------------------------------------- 1 | #include "inc/buttons/bt_os_hardware_buttons_manual_backend.hpp" 2 | 3 | #include "utils/CallbackConnector.hpp" 4 | #include "utils/MetaUtils.hpp" 5 | 6 | namespace Buttons 7 | { 8 | 9 | OsTimerBackend::OsTimerBackend() : m_isTimerEllapsed{true} 10 | { 11 | } 12 | 13 | void OsTimerBackend::startTimer() 14 | { 15 | } 16 | 17 | void OsTimerBackend::stopTimer() 18 | { 19 | } 20 | 21 | void OsTimerBackend::initialize() 22 | { 23 | } 24 | 25 | bool OsTimerBackend::isTimerEllapsed() const 26 | { 27 | return m_isTimerEllapsed; 28 | } 29 | 30 | OsButtonsBackend::OsButtonsBackend() 31 | { 32 | } 33 | 34 | void OsButtonsBackend::initialize() 35 | { 36 | } 37 | 38 | } // namespace Buttons 39 | -------------------------------------------------------------------------------- /Firmware/drivers/buttons/inc/buttons/bt_firmware_simulator_hardware_buttons_manual_backend.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/drivers/ih_button_driver.hpp" 4 | 5 | #include 6 | 7 | namespace Buttons 8 | { 9 | class FirmwareSimulatorTimerBackend 10 | { 11 | 12 | public: 13 | FirmwareSimulatorTimerBackend(); 14 | 15 | public: 16 | Simple::Signal onTimerExpired; 17 | 18 | public: 19 | bool isTimerEllapsed() const; 20 | 21 | void startTimer(); 22 | 23 | void stopTimer(); 24 | 25 | void initialize(); 26 | 27 | private: 28 | static constexpr inline std::uint32_t ClicksDetectionPeriodMs = 400; 29 | static constexpr inline std::uint32_t TimerId = 128; 30 | std::atomic m_isTimerEllapsed = false; 31 | }; 32 | 33 | class FirmwareSimulatorButtonsBackend 34 | { 35 | 36 | public: 37 | FirmwareSimulatorButtonsBackend(); 38 | 39 | public: 40 | Simple::Signal onButtonEvent; 41 | 42 | public: 43 | void initialize(); 44 | private: 45 | void initInternals(); 46 | }; 47 | 48 | } // namespace Buttons 49 | -------------------------------------------------------------------------------- /Firmware/drivers/buttons/inc/buttons/bt_nordic_hardware_backend.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/drivers/ih_button_driver.hpp" 4 | 5 | #include 6 | 7 | namespace Buttons 8 | { 9 | 10 | class NordicTimerBackend 11 | { 12 | 13 | public: 14 | Simple::Signal onTimerExpired; 15 | 16 | public: 17 | bool isTimerEllapsed() const; 18 | 19 | void startTimer(); 20 | 21 | void stopTimer(); 22 | 23 | void initialize(); 24 | 25 | private: 26 | static std::uint32_t convertToTimerTicks(std::chrono::milliseconds _interval); 27 | 28 | private: 29 | const std::chrono::milliseconds m_buttonLongPressDetectionDelay = 30 | std::chrono::milliseconds{400}; 31 | 32 | std::atomic m_isTimerEllapsed = false; 33 | std::atomic m_isInitialized = false; 34 | }; 35 | 36 | class NordicButtonsBackend 37 | { 38 | 39 | public: 40 | void initialize(); 41 | 42 | public: 43 | Simple::Signal onButtonEvent; 44 | 45 | private: 46 | void initNordicButtonsBackend(); 47 | 48 | void handleHardwareButtonEvent(std::uint8_t _pinNumber, std::uint8_t _buttonEvent); 49 | }; 50 | 51 | } // namespace Buttons -------------------------------------------------------------------------------- /Firmware/drivers/display/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | watch_display 3 | INTERFACE 4 | ) 5 | 6 | target_include_directories( 7 | watch_display 8 | INTERFACE 9 | ${CMAKE_CURRENT_LIST_DIR}/inc 10 | ) 11 | 12 | target_include_directories( 13 | watch_display 14 | INTERFACE 15 | UtilsLibrary 16 | ${CMAKE_CURRENT_LIST_DIR} 17 | ) 18 | 19 | 20 | target_link_libraries( 21 | watch_display 22 | INTERFACE 23 | spi 24 | UtilsLibrary 25 | drivers_ih 26 | platform_delay 27 | gpio 28 | etl 29 | logger_service 30 | ) -------------------------------------------------------------------------------- /Firmware/drivers/factory_impl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | ble_factory 3 | ble_services_stub.cpp 4 | ih_ble_service_factory.cpp 5 | ih_ible_softdevice.cpp 6 | ) 7 | 8 | target_include_directories( 9 | ble_factory 10 | PUBLIC 11 | ${CMAKE_CURRENT_LIST_DIR}/inc 12 | ) 13 | 14 | target_link_libraries( 15 | ble_factory 16 | PRIVATE 17 | UtilsLibrary 18 | drivers_ih 19 | logger_service 20 | ble_services 21 | ) 22 | 23 | if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) 24 | target_link_libraries( 25 | ble_factory 26 | PRIVATE 27 | NordicSDK::Common 28 | ble_services 29 | ) 30 | target_compile_definitions( 31 | ble_factory 32 | PRIVATE 33 | USE_BLE_SERVICES 34 | ) 35 | elseif( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR" ) 36 | target_compile_definitions( 37 | ble_factory 38 | PRIVATE 39 | USE_DESKTOP_SIMULATOR 40 | ) 41 | endif() -------------------------------------------------------------------------------- /Firmware/drivers/factory_impl/ble_services_stub.cpp: -------------------------------------------------------------------------------- 1 | #include "ble_services_stub.hpp" 2 | 3 | #include "logger/logger_service.hpp" 4 | #include "utils/MetaUtils.hpp" 5 | 6 | namespace Ble::BatteryService 7 | { 8 | 9 | StubBatteryService::StubBatteryService() noexcept 10 | { 11 | LOG_DEBUG_ENDL("Initialized DesktopStubBatteryService"); 12 | } 13 | 14 | void StubBatteryService::onBatteryLevelChanged(std::uint8_t _newBatteryLevel) noexcept 15 | { 16 | LOG_DEBUG("New battery level is:"); 17 | LOG_DEBUG_ENDL(_newBatteryLevel); 18 | } 19 | 20 | } // namespace Ble::BatteryService 21 | 22 | namespace Ble::DateTimeService 23 | { 24 | 25 | StubDateTimeService::StubDateTimeService() noexcept 26 | { 27 | LOG_DEBUG_ENDL("Initialized StubDateTimeService"); 28 | } 29 | 30 | void StubDateTimeService::handleDiscoveryEvent(const std::any& _pBleDbDiscoveryEvent) noexcept 31 | { 32 | Meta::UnuseVar(_pBleDbDiscoveryEvent); 33 | } 34 | 35 | } // namespace Ble::DateTimeService 36 | 37 | namespace Ble::HeartrateService 38 | { 39 | 40 | StubHeartrateService::StubHeartrateService() noexcept 41 | { 42 | } 43 | 44 | void StubHeartrateService::onHeartrateChanged(std::uint8_t _heartRate) noexcept 45 | { 46 | } 47 | 48 | void StubHeartrateService::setSensorLocation(SensorLocation _location) noexcept 49 | { 50 | } 51 | 52 | } // namespace Ble::HeartrateService 53 | -------------------------------------------------------------------------------- /Firmware/drivers/factory_impl/ble_services_stub.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/drivers/ih_ble_battery_service.hpp" 4 | #include "ih/drivers/ih_ble_dts_service.hpp" 5 | #include "ih/drivers/ih_ble_heartrate_service.hpp" 6 | 7 | namespace Ble::BatteryService 8 | { 9 | 10 | class StubBatteryService : public IBatteryLevelService 11 | { 12 | 13 | public: 14 | StubBatteryService() noexcept; 15 | ~StubBatteryService() override = default; 16 | 17 | public: 18 | void onBatteryLevelChanged(std::uint8_t _newBatteryLevel) noexcept override; 19 | }; 20 | 21 | } // namespace Ble::BatteryService 22 | 23 | namespace Ble::DateTimeService 24 | { 25 | class StubDateTimeService : public IDateTimeService 26 | { 27 | 28 | public: 29 | StubDateTimeService() noexcept; 30 | 31 | public: 32 | void handleDiscoveryEvent(const std::any& _pBleDbDiscoveryEvent) noexcept override; 33 | 34 | public: 35 | virtual ~StubDateTimeService() override = default; 36 | }; 37 | 38 | } // namespace Ble::DateTimeService 39 | 40 | namespace Ble::HeartrateService 41 | { 42 | class StubHeartrateService : public IHeartrateService 43 | { 44 | 45 | public: 46 | StubHeartrateService() noexcept; 47 | 48 | public: 49 | virtual ~StubHeartrateService() override = default; 50 | 51 | public: 52 | void onHeartrateChanged(std::uint8_t _newHeartrateLevel) noexcept override; 53 | 54 | void setSensorLocation(SensorLocation _sensorLocation) noexcept override; 55 | }; 56 | 57 | } // namespace Ble::HeartrateService -------------------------------------------------------------------------------- /Firmware/drivers/factory_impl/ih_ible_softdevice.cpp: -------------------------------------------------------------------------------- 1 | #include "ih/drivers/ih_ible_softdevice.hpp" 2 | 3 | #if defined(USE_BLE_SERVICES) 4 | #include "ble/nordic_ble/ble_custom_service.hpp" 5 | #include "ble/nordic_ble/ble_softdevice.hpp" 6 | 7 | #elif defined(USE_DESKTOP_SIMULATOR) 8 | #include "desktop_ble/ble_desktop_softdevice.hpp" 9 | #endif 10 | 11 | namespace Ble::Stack 12 | { 13 | 14 | TSoftDevicePtr createSoftDevice(Ble::ServiceFactory::TBleFactoryPtr&& _pServiceCreator) noexcept 15 | { 16 | return Ble::Stack::createBleStackKeeper(std::move(_pServiceCreator)); 17 | } 18 | 19 | } // namespace Ble::Stack -------------------------------------------------------------------------------- /Firmware/drivers/gpio/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | gpio 3 | gpio_pin.cpp 4 | ) 5 | 6 | target_include_directories( 7 | gpio 8 | PUBLIC 9 | ${CMAKE_CURRENT_LIST_DIR}/inc 10 | ) 11 | 12 | target_link_libraries( 13 | gpio 14 | PRIVATE 15 | UtilsLibrary 16 | logger_service 17 | ) 18 | 19 | if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) 20 | target_compile_definitions(gpio PUBLIC USE_DEVICE_SPECIFIC) 21 | target_link_libraries(gpio PRIVATE NordicSDK::Common ) 22 | endif() -------------------------------------------------------------------------------- /Firmware/drivers/gpio/gpio_pin.cpp: -------------------------------------------------------------------------------- 1 | #include "inc/gpio/gpio_pin.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #if defined(USE_DEVICE_SPECIFIC) 7 | #include "boards.h" 8 | #endif 9 | 10 | namespace Gpio 11 | { 12 | 13 | #if defined(USE_DEVICE_SPECIFIC) 14 | template 15 | GpioPin::GpioPin() noexcept 16 | { 17 | if (m_pinDirection == Direction::Output) 18 | nrf_gpio_cfg_output(m_pinNumber); 19 | } 20 | 21 | template 22 | void GpioPin::set() noexcept 23 | { 24 | nrf_gpio_pin_set(m_pinNumber); 25 | } 26 | 27 | template 28 | void GpioPin::reset() noexcept 29 | { 30 | nrf_gpio_pin_clear(m_pinNumber); 31 | } 32 | 33 | #else 34 | template 35 | GpioPin::GpioPin() noexcept = default; 36 | 37 | template 38 | void GpioPin::set() noexcept 39 | { 40 | LOG_DEBUG_ENDL("Gpio Set called"); 41 | } 42 | 43 | template 44 | void GpioPin::reset() noexcept 45 | { 46 | LOG_DEBUG_ENDL("Gpio Reset called"); 47 | } 48 | 49 | #endif 50 | 51 | template 52 | GpioPin::~GpioPin() = default; 53 | 54 | template class GpioPin; 55 | template class GpioPin; 56 | template class GpioPin; 57 | }; // namespace Gpio 58 | -------------------------------------------------------------------------------- /Firmware/drivers/gpio/inc/gpio/gpio_pin.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #if defined(USE_DEVICE_SPECIFIC) 6 | #include "pca10040.h" 7 | #endif 8 | 9 | namespace Gpio::Pins 10 | { 11 | #if defined(USE_DEVICE_SPECIFIC) 12 | inline constexpr std::uint8_t Display_DataCommand = DISP_DC_PIN; 13 | inline constexpr std::uint8_t Display_Reset = DISP_RST; 14 | inline constexpr std::uint8_t LedPin = 13; 15 | #else 16 | inline constexpr std::uint8_t Display_DataCommand = 26; 17 | inline constexpr std::uint8_t Display_Reset = 31; 18 | inline constexpr std::uint8_t LedPin = 13; 19 | #endif 20 | 21 | } // namespace Gpio::Pins 22 | 23 | namespace Gpio 24 | { 25 | 26 | enum class Direction 27 | { 28 | Input, 29 | Output 30 | }; 31 | 32 | template class GpioPin 33 | { 34 | 35 | public: 36 | GpioPin() noexcept; 37 | 38 | ~GpioPin(); 39 | 40 | public: 41 | void set() noexcept; 42 | 43 | void reset() noexcept; 44 | 45 | private: 46 | Direction m_pinDirection = pinDirection; 47 | std::uint8_t m_pinNumber = GpioPinNumber; 48 | }; 49 | 50 | using DisplayDataCommandPin = GpioPin; 51 | using DisplayResetPin = GpioPin; 52 | using OnboardLedPin = GpioPin; 53 | 54 | } // namespace Gpio 55 | -------------------------------------------------------------------------------- /Firmware/drivers/headers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( drivers_ih INTERFACE ) 2 | target_include_directories( drivers_ih INTERFACE ${CMAKE_CURRENT_LIST_DIR} ) 3 | target_compile_features( drivers_ih INTERFACE cxx_std_20 ) -------------------------------------------------------------------------------- /Firmware/drivers/headers/ih/drivers/ih_ble_battery_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils/Noncopyable.hpp" 4 | #include 5 | 6 | namespace Ble::BatteryService 7 | { 8 | 9 | class IBatteryLevelService : private Utils::noncopyable 10 | { 11 | 12 | public: 13 | virtual ~IBatteryLevelService() = default; 14 | 15 | public: 16 | virtual void onBatteryLevelChanged(std::uint8_t _newBatteryLevel) noexcept = 0; 17 | }; 18 | 19 | } // namespace Ble::BatteryService -------------------------------------------------------------------------------- /Firmware/drivers/headers/ih/drivers/ih_ble_dts_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils/Noncopyable.hpp" 4 | #include "utils/SimpleSignal.hpp" 5 | #include "utils/TimeWrapper.hpp" 6 | 7 | #include 8 | 9 | namespace Ble::DateTimeService 10 | { 11 | 12 | class IDateTimeService : private Utils::noncopyable 13 | { 14 | 15 | public: 16 | virtual ~IDateTimeService() = default; 17 | 18 | public: 19 | virtual void handleDiscoveryEvent(const std::any& _pBleDbDiscoveryEvent) noexcept = 0; 20 | 21 | public: 22 | Simple::Signal onDateTimeDiscovered; 23 | }; 24 | 25 | } // namespace Ble::DateTimeService -------------------------------------------------------------------------------- /Firmware/drivers/headers/ih/drivers/ih_ble_heartrate_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils/Noncopyable.hpp" 4 | #include "utils/SimpleSignal.hpp" 5 | #include "utils/TimeWrapper.hpp" 6 | 7 | #include 8 | 9 | namespace Ble::HeartrateService 10 | { 11 | 12 | enum class SensorLocation 13 | { 14 | Finger, 15 | Chest, 16 | Hand 17 | }; 18 | 19 | class IHeartrateService : private Utils::noncopyable 20 | { 21 | 22 | public: 23 | virtual ~IHeartrateService() = default; 24 | 25 | public: 26 | virtual void onHeartrateChanged(std::uint8_t _newHeartrateLevel) noexcept = 0; 27 | 28 | virtual void setSensorLocation(SensorLocation _sensorLocation) noexcept = 0; 29 | }; 30 | 31 | } // namespace Ble::HeartrateService -------------------------------------------------------------------------------- /Firmware/drivers/headers/ih/drivers/ih_ble_service_factory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/drivers/ih_ble_battery_service.hpp" 4 | #include "ih/drivers/ih_ble_dts_service.hpp" 5 | 6 | #include "utils/Noncopyable.hpp" 7 | 8 | #include 9 | #include 10 | 11 | namespace Ble::BatteryService 12 | { 13 | class IBatteryLevelService; 14 | } 15 | 16 | namespace Ble::DateTimeService 17 | { 18 | class IDateTimeService; 19 | } 20 | 21 | namespace Ble::HeartrateService 22 | { 23 | class IHeartrateService; 24 | } 25 | 26 | namespace Ble::ServiceFactory 27 | { 28 | 29 | class IBleServiceFactory : private Utils::noncopyable 30 | { 31 | public: 32 | virtual ~IBleServiceFactory() = default; 33 | 34 | public: 35 | template using TServicePtr = std::unique_ptr; 36 | 37 | using TBatteryServicePtr = TServicePtr; 38 | [[nodiscard]] virtual TBatteryServicePtr getBatteryService() noexcept = 0; 39 | 40 | using TDateTimeServicePtr = TServicePtr; 41 | [[nodiscard]] virtual TDateTimeServicePtr getDateTimeService( 42 | const std::any& _gattQueue) noexcept = 0; 43 | 44 | using THeartrateServicePtr = TServicePtr; 45 | [[nodiscard]] virtual THeartrateServicePtr getHeartrateService() noexcept = 0; 46 | }; 47 | 48 | using TBleFactoryPtr = std::unique_ptr; 49 | TBleFactoryPtr getBleServiceFactory() noexcept; 50 | 51 | } // namespace Ble::ServiceFactory 52 | -------------------------------------------------------------------------------- /Firmware/drivers/headers/ih/drivers/ih_ible_softdevice.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/drivers/ih_ble_service_factory.hpp" 4 | #include "utils/SimpleSignal.hpp" 5 | 6 | namespace Ble::BatteryService 7 | { 8 | class IBatteryLevelService; 9 | } 10 | 11 | namespace Ble::DateTimeService 12 | { 13 | class IDateTimeService; 14 | } 15 | 16 | namespace Ble::Stack 17 | { 18 | 19 | class IBleSoftDevice 20 | { 21 | 22 | public: 23 | virtual ~IBleSoftDevice() = default; 24 | 25 | public: 26 | virtual Ble::BatteryService::IBatteryLevelService& getBatteryService() noexcept = 0; 27 | 28 | virtual const Ble::BatteryService::IBatteryLevelService& getBatteryService() const noexcept = 0; 29 | 30 | virtual Ble::DateTimeService::IDateTimeService& getDateTimeService() noexcept = 0; 31 | 32 | virtual const Ble::DateTimeService::IDateTimeService& getDateTimeService() const noexcept = 0; 33 | 34 | public: 35 | Simple::Signal onConnected; 36 | Simple::Signal onDisconnected; 37 | }; 38 | 39 | using TSoftDevicePtr = std::unique_ptr; 40 | [[nodiscard]] TSoftDevicePtr createSoftDevice( 41 | Ble::ServiceFactory::TBleFactoryPtr&& _pServiceCreator) noexcept; 42 | 43 | } // namespace Ble::Stack -------------------------------------------------------------------------------- /Firmware/drivers/headers/ih/drivers/transaction_item.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace Interface::Spi 8 | { 9 | 10 | struct Transaction 11 | { 12 | std::function beforeTransaction; 13 | std::function afterTransaction; 14 | std::function transactionAction; 15 | 16 | size_t repeatsCount; 17 | }; 18 | 19 | struct TransactionDescriptor 20 | { 21 | std::function beforeTransaction; 22 | std::function afterTransaction; 23 | 24 | struct DataSequence 25 | { 26 | const std::uint8_t* dataBlock; 27 | const size_t blockSize; 28 | }; 29 | 30 | std::variant transactionData; 31 | }; 32 | 33 | }; // namespace Interface::Spi 34 | -------------------------------------------------------------------------------- /Firmware/drivers/i2c/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | i2c 3 | i2c_test.cpp 4 | ) 5 | 6 | target_compile_features( 7 | i2c 8 | INTERFACE 9 | cxx_std_17 10 | ) 11 | 12 | target_include_directories( 13 | i2c 14 | PUBLIC 15 | ${CMAKE_CURRENT_LIST_DIR}/inc 16 | ) 17 | 18 | target_link_libraries( 19 | i2c 20 | PRIVATE 21 | UtilsLibrary 22 | logger_service 23 | ) 24 | 25 | if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) 26 | target_link_libraries(i2c PRIVATE NordicSDK::Common ) 27 | 28 | target_compile_definitions( 29 | i2c 30 | PRIVATE 31 | USE_DEVICE_SPECIFIC 32 | ) 33 | endif() -------------------------------------------------------------------------------- /Firmware/drivers/i2c/i2c_test.cpp: -------------------------------------------------------------------------------- 1 | #include "inc/i2c/i2c_test.hpp" 2 | 3 | #if defined(USE_DEVICE_SPECIFIC) 4 | #include "nrf_drv_twi.h" 5 | #include "pca10040.h" 6 | 7 | #endif 8 | 9 | #include "utils/MetaUtils.hpp" 10 | 11 | #include "logger/logger_service.hpp" 12 | 13 | namespace I2C 14 | { 15 | void scanI2CSensors() 16 | { 17 | #if defined(USE_DEVICE_SPECIFIC) 18 | static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(0); 19 | 20 | ret_code_t errorCode{}; 21 | 22 | const nrf_drv_twi_config_t twiMpuConfig = { 23 | .scl = MPU_SCL_PIN, 24 | .sda = MPU_SDA_PIN, 25 | .frequency = NRF_DRV_TWI_FREQ_100K, 26 | .interrupt_priority = APP_IRQ_PRIORITY_HIGH, 27 | .clear_bus_init = false}; 28 | 29 | errorCode = nrf_drv_twi_init(&m_twi, &twiMpuConfig, nullptr, nullptr); 30 | APP_ERROR_CHECK(errorCode); 31 | 32 | nrf_drv_twi_enable(&m_twi); 33 | 34 | nrf_gpio_cfg_output(MPU_ADO_PIN); 35 | nrf_gpio_pin_set(MPU_ADO_PIN); 36 | 37 | constexpr std::uint8_t Mpu9250Addr = 0x69; 38 | std::uint8_t SampleData = 0x00; 39 | 40 | errorCode = nrf_drv_twi_rx(&m_twi, Mpu9250Addr, &SampleData, sizeof(SampleData)); 41 | 42 | if (errorCode == NRF_SUCCESS) 43 | LOG_DEBUG_ENDL("MPU9250 detected on address 0x69"); 44 | 45 | constexpr std::uint8_t Max30102Addr = 0x57; 46 | 47 | errorCode = nrf_drv_twi_rx(&m_twi, Max30102Addr, &SampleData, sizeof(SampleData)); 48 | 49 | if (errorCode == NRF_SUCCESS) 50 | LOG_DEBUG_ENDL("Max30102 detected on address 0x57"); 51 | #endif 52 | } 53 | 54 | }; // namespace I2C 55 | -------------------------------------------------------------------------------- /Firmware/drivers/i2c/inc/i2c/i2c_test.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils/FastPimpl.hpp" 4 | #include "utils/Platform.hpp" 5 | 6 | namespace I2C 7 | { 8 | 9 | void scanI2CSensors(); 10 | 11 | } // namespace I2C 12 | -------------------------------------------------------------------------------- /Firmware/drivers/platform_delay/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | platform_delay 3 | delay_provider.cpp 4 | ) 5 | 6 | target_include_directories( 7 | platform_delay 8 | PUBLIC 9 | ${CMAKE_CURRENT_LIST_DIR}/inc 10 | ) 11 | 12 | if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) 13 | target_compile_definitions( platform_delay PRIVATE USE_DEVICE_SPECIFIC ) 14 | target_link_libraries( platform_delay PRIVATE NordicSDK::Common ) 15 | endif() -------------------------------------------------------------------------------- /Firmware/drivers/platform_delay/delay_provider.cpp: -------------------------------------------------------------------------------- 1 | #include "inc/delay/delay_provider.hpp" 2 | 3 | #if defined(USE_DEVICE_SPECIFIC) 4 | #include "nrf_delay.h" 5 | #else 6 | #include 7 | #include 8 | #endif 9 | 10 | namespace Delay 11 | { 12 | void waitFor(std::uint16_t _toWaitBlockingMs) 13 | { 14 | #if defined(USE_DEVICE_SPECIFIC) 15 | nrf_delay_ms(_toWaitBlockingMs); 16 | #else 17 | std::this_thread::sleep_for(std::chrono::milliseconds(_toWaitBlockingMs)); 18 | #endif 19 | } 20 | } // namespace Delay 21 | -------------------------------------------------------------------------------- /Firmware/drivers/platform_delay/inc/delay/delay_provider.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Delay 6 | { 7 | void waitFor(std::uint16_t _toWaitBlocking); 8 | } // namespace Delay 9 | -------------------------------------------------------------------------------- /Firmware/drivers/spi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | spi 3 | INTERFACE 4 | ) 5 | 6 | if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) 7 | target_link_libraries( spi INTERFACE NordicSDK::Common ) 8 | elseif( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR") 9 | target_link_libraries( spi INTERFACE fmt::fmt ) 10 | endif() 11 | 12 | target_compile_options( 13 | spi 14 | INTERFACE 15 | $<$:-fcoroutines> 16 | ) 17 | 18 | target_include_directories( 19 | spi 20 | INTERFACE 21 | ${CMAKE_CURRENT_LIST_DIR}/inc 22 | ${CMAKE_CURRENT_LIST_DIR}/inc/backends 23 | ) 24 | 25 | target_link_libraries( 26 | spi 27 | INTERFACE 28 | UtilsLibrary 29 | etl 30 | logger_service 31 | ) 32 | target_compile_features( 33 | spi 34 | INTERFACE 35 | cxx_std_20 36 | ) 37 | -------------------------------------------------------------------------------- /Firmware/drivers/winbondflash/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | windbond_spi_flash_driver 3 | INTERFACE 4 | ) 5 | 6 | target_include_directories( 7 | windbond_spi_flash_driver 8 | INTERFACE 9 | ${CMAKE_CURRENT_LIST_DIR}/inc 10 | ) -------------------------------------------------------------------------------- /Firmware/drivers/winbondflash/inc/windbondflash/winbond_commandset.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | namespace WindbondCommandSet 7 | { 8 | constexpr std::uint8_t WriteEnable = 0x06; 9 | constexpr std::uint8_t WriteDisable = 0x04; 10 | 11 | constexpr std::uint8_t ReadStatusRegister1 = 0x05; 12 | constexpr std::uint8_t ReadStatusRegister2 = 0x35; 13 | 14 | constexpr std::uint8_t WriteStatusRegister = 0x01; 15 | 16 | constexpr std::uint8_t ReadData = 0x03; 17 | constexpr std::uint8_t FastRead = 0x0b; 18 | constexpr std::uint8_t PageProgram = 0x02; 19 | 20 | constexpr std::uint8_t SectorErase4KB = 0x20; 21 | constexpr std::uint8_t BlockErase32KB = 0x52; 22 | constexpr std::uint8_t BlockErase64KB = 0xd8; 23 | 24 | constexpr std::uint8_t ChipErase = 0xc7; 25 | constexpr std::uint8_t ChipEraseSimilar = 0x60; 26 | 27 | constexpr std::uint8_t EraseSuspend = 0x75; 28 | constexpr std::uint8_t EraseResume = 0x7A; 29 | 30 | constexpr std::uint8_t PowerDownMode = 0xb9; 31 | constexpr std::uint8_t ResumePowerDownMode = 0xab; 32 | 33 | constexpr std::uint8_t DeviceIdFromPowedDownRelease = 0xab; 34 | constexpr std::uint8_t ReadManufactureId = 0x90; 35 | 36 | constexpr std::uint8_t JedecIdLength = 3; 37 | constexpr std::uint8_t ReadJedecId = 0x9F; 38 | 39 | constexpr std::uint8_t UniqueIdLength = 8; // 64 bits 40 | constexpr std::uint8_t ReadUniqueId = 0x4B; 41 | 42 | constexpr std::uint8_t DummyByte = 0x00; 43 | } 44 | -------------------------------------------------------------------------------- /Firmware/firmware_tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(FirmwareTesting LANGUAGES CXX) 2 | 3 | 4 | include(GoogleTest) 5 | 6 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 7 | 8 | set(CMAKE_CXX_STANDARD 20) 9 | set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 10 | set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 11 | 12 | enable_testing() 13 | set (3RDPARTY_DIR "../3rdparty") 14 | find_package(GTest REQUIRED) 15 | add_subdirectory(coroutine) 16 | add_subdirectory(article_example) 17 | 18 | add_executable( 19 | ${PROJECT_NAME} 20 | 21 | buttons_driver/buttons_driver_test.cpp 22 | 23 | clock_page_handler/clock_page_view_handler_test.cpp 24 | 25 | main_window/mainwindow_model_test.cpp 26 | 27 | stubs/pages_stub_pages_creator.cpp 28 | stubs/widgets_stub_pages_creator.cpp 29 | 30 | drivers/spi/spi_driver_fixture.hpp 31 | drivers/spi/spi_fake_backend.hpp 32 | drivers/spi/spi_driver_test_suite.cpp 33 | 34 | drivers/windond_flash/flash_driver_test_suite.cpp 35 | drivers/windond_flash/flash_fixture.hpp 36 | drivers/spi/mock_gpio.hpp 37 | drivers/spi/mock_spi.hpp 38 | ) 39 | 40 | mark_as_advanced( 41 | BUILD_GMOCK BUILD_GTEST BUILD_SHARED_LIBS 42 | gmock_build_tests gtest_build_samples gtest_build_tests 43 | gtest_disable_pthreads gtest_force_shared_crt gtest_hide_internal_symbols 44 | ) 45 | 46 | target_include_directories( 47 | ${PROJECT_NAME} 48 | PUBLIC 49 | stubs 50 | stubs/pages 51 | stubs/widgets 52 | ) 53 | 54 | target_link_libraries( 55 | ${PROJECT_NAME} 56 | GTest::gtest 57 | GTest::gmock 58 | GTest::gtest_main 59 | GTest::gmock_main 60 | UtilsLibrary 61 | 62 | watch_graphics 63 | watch_display 64 | lvgl_lib 65 | drivers_ih 66 | 67 | buttons_driver 68 | windbond_spi_flash_driver 69 | ) 70 | 71 | target_compile_features( 72 | ${PROJECT_NAME} 73 | PUBLIC 74 | cxx_std_20 75 | ) 76 | 77 | gtest_discover_tests(${PROJECT_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) -------------------------------------------------------------------------------- /Firmware/firmware_tests/article_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable( 2 | ArticleExample 3 | root.cpp 4 | ) 5 | 6 | target_include_directories( 7 | ArticleExample 8 | PRIVATE 9 | ${CMAKE_CURRENT_LIST_DIR} 10 | ) 11 | 12 | target_compile_features( 13 | ArticleExample 14 | PUBLIC 15 | cxx_std_20 16 | ) 17 | 18 | target_compile_options(ArticleExample PUBLIC $<$:-fcoroutines>) 19 | -------------------------------------------------------------------------------- /Firmware/firmware_tests/article_example/coroutine_utils.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Firmware/firmware_tests/article_example/coroutine_utils.hpp -------------------------------------------------------------------------------- /Firmware/firmware_tests/article_example/gpio.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace Gpio::Pins 5 | { 6 | inline constexpr std::uint8_t Display_DataCommand = 0; 7 | inline constexpr std::uint8_t Display_Reset = 0; 8 | inline constexpr std::uint8_t LedPin = 0; 9 | 10 | } // namespace Gpio::Pins 11 | 12 | namespace Gpio 13 | { 14 | 15 | enum class Direction 16 | { 17 | Input, 18 | Output 19 | }; 20 | 21 | template class GpioPin 22 | { 23 | 24 | public: 25 | GpioPin() noexcept = default; 26 | 27 | ~GpioPin() = default; 28 | 29 | public: 30 | void set() noexcept 31 | { 32 | std::cout << "GPIO SET CALLED\n"; 33 | } 34 | 35 | void reset() noexcept 36 | { 37 | std::cout << "GPIO RESET CALLED\n"; 38 | } 39 | 40 | private: 41 | Direction m_pinDirection = pinDirection; 42 | std::uint8_t m_pinNumber = GpioPinNumber; 43 | }; 44 | 45 | using DisplayDataCommandPin = GpioPin; 46 | using DisplayResetPin = GpioPin; 47 | using OnboardLedPin = GpioPin; 48 | 49 | } // namespace Gpio -------------------------------------------------------------------------------- /Firmware/firmware_tests/article_example/root.cpp: -------------------------------------------------------------------------------- 1 | // This file is a "Hello, world!" in C++ language by GCC for wandbox. 2 | #include "coroutine_utils.hpp" 3 | #include "display_gc9a01.hpp" 4 | #include "spi_desktop_backend.hpp" 5 | #include "spi_driver_high_level.hpp" 6 | #include 7 | #include 8 | 9 | using TSpiBus = SpiBus; 10 | using TDisplayDriver = DisplayDriver::GC9A01Compact; 11 | 12 | TDisplayDriver compactGc9A01; 13 | 14 | CoroUtils::Task waitForInit() 15 | { 16 | compactGc9A01.initialize(); 17 | co_await compactGc9A01.displayInitialized; 18 | co_return 0; 19 | } 20 | 21 | int main() 22 | { 23 | auto waitForInitTask = waitForInit(); 24 | int initResult = CoroUtils::syncWait(waitForInitTask); 25 | return initResult; 26 | } -------------------------------------------------------------------------------- /Firmware/firmware_tests/article_example/spi_desktop_backend.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | class SpiBusDesktopBackend 12 | { 13 | 14 | public: 15 | SpiBusDesktopBackend() = default; 16 | 17 | ~SpiBusDesktopBackend() = default; 18 | 19 | public: 20 | void sendChunk(const std::uint8_t* _pBuffer, const size_t _bufferSize) noexcept 21 | { 22 | m_dataBuffer = std::span(_pBuffer, _bufferSize); 23 | std::cout << "TRANSMIT SOME DATA:" << ' '; 24 | 25 | for (const auto block : m_dataBuffer) 26 | { 27 | std::cout << std::hex << static_cast(block) << ' '; 28 | } 29 | std::cout << std::endl; 30 | 31 | m_transactionCompleted(); 32 | } 33 | 34 | using TTransactionCompletedHandler = std::function; 35 | void setTransactionCompletedHandler(TTransactionCompletedHandler&& _handler) 36 | { 37 | m_transactionCompleted = std::move(_handler); 38 | } 39 | 40 | void xferChunk( 41 | std::span _transmitArray, 42 | std::span _receiveArray) 43 | { 44 | } 45 | 46 | private: 47 | std::span m_dataBuffer; 48 | 49 | TTransactionCompletedHandler m_transactionCompleted; 50 | }; -------------------------------------------------------------------------------- /Firmware/firmware_tests/buttons_driver/buttons_fake_backends.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/drivers/ih_button_driver.hpp" 4 | 5 | namespace Buttons 6 | { 7 | 8 | class FakeTimerBackend 9 | { 10 | 11 | public: 12 | Simple::Signal onTimerExpired; 13 | 14 | public: 15 | bool isTimerEllapsed() const 16 | { 17 | return m_isTimerEllapsed; 18 | }; 19 | 20 | void startTimer() 21 | { 22 | m_isTimerEllapsed = false; 23 | } 24 | 25 | void stopTimer() 26 | { 27 | m_isTimerEllapsed = true; 28 | } 29 | 30 | public: 31 | void ellapseTimer() 32 | { 33 | m_isTimerEllapsed = true; 34 | onTimerExpired.emit(); 35 | } 36 | 37 | private: 38 | bool m_isTimerEllapsed = false; 39 | }; 40 | 41 | class FakeButtonsBackend 42 | { 43 | 44 | public: 45 | Simple::Signal onButtonEvent; 46 | 47 | public: 48 | void fakeButtonPress(ButtonId _buttonId) 49 | { 50 | onButtonEvent.emit(_buttonId, ButtonBackendEvent::kPressed); 51 | } 52 | void fakeButtonRelease(ButtonId _buttonId) 53 | { 54 | onButtonEvent.emit(_buttonId, ButtonBackendEvent::kReleased); 55 | } 56 | }; 57 | 58 | } // namespace Buttons -------------------------------------------------------------------------------- /Firmware/firmware_tests/buttons_driver/buttons_fake_event_handler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_events.hpp" 4 | #include "ih/gs_ievent_handler.hpp" 5 | 6 | #include "widgets_layer/gs_event_handler_base.hpp" 7 | 8 | namespace FakeButton 9 | { 10 | 11 | class FakeButtonEventsHandler 12 | : public Graphics::Events:: 13 | EventHandler 14 | { 15 | 16 | public: 17 | explicit FakeButtonEventsHandler() : m_button{} {}; 18 | 19 | ~FakeButtonEventsHandler() override = default; 20 | 21 | public: 22 | bool hasEvents() const 23 | { 24 | return !m_receivedEvents.empty(); 25 | } 26 | 27 | size_t getEventsCount() const 28 | { 29 | return m_receivedEvents.size(); 30 | } 31 | 32 | Graphics::Events::TButtonsEvents getEventAt(size_t _eventIndex) 33 | { 34 | return m_receivedEvents.at(_eventIndex); 35 | } 36 | 37 | Graphics::Events::TButtonsEvents getLastEvent() const 38 | { 39 | return m_receivedEvents.back(); 40 | } 41 | 42 | Buttons::ButtonId getLastButton() const 43 | { 44 | return m_button; 45 | } 46 | 47 | protected: 48 | void handleEventImpl( 49 | const Graphics::Events::TButtonsEvents& _event, 50 | const std::any& _eventData) noexcept override 51 | { 52 | m_receivedEvents.push_back(_event); 53 | m_button = std::any_cast(_eventData); 54 | } 55 | 56 | private: 57 | using TEventsStorage = std::vector; 58 | TEventsStorage m_receivedEvents; 59 | Buttons::ButtonId m_button; 60 | }; 61 | 62 | using TFakeButtonsHandlerPtr = std::unique_ptr; 63 | 64 | TFakeButtonsHandlerPtr createFakeButtonsHandler() noexcept 65 | { 66 | return std::make_unique(); 67 | } 68 | 69 | } // namespace FakeButton -------------------------------------------------------------------------------- /Firmware/firmware_tests/clock_page_handler/clock_page_handler_fixture.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "widgets_layer/pages/clock_page/gs_clock_page_handler.hpp" 5 | 6 | #include "clock_watch_fake_view.hpp" 7 | 8 | #include 9 | 10 | class ClockPageHandlerTest : public ::testing::Test 11 | { 12 | 13 | protected: 14 | void SetUp() override 15 | { 16 | setupTimeZoneEnvironment(); 17 | 18 | pageWatchHandler = Graphics::Views::createPageWatchHandler(&fakeView); 19 | pageMockWatchHandler = Graphics::Views::createPageWatchHandler(&fakePageMock); 20 | } 21 | 22 | void setDefaultDate() 23 | { 24 | using ::testing::Return; 25 | 26 | fakeView.show(); 27 | 28 | pageWatchHandler->handleEvent( 29 | {Graphics::Events::EventGroup::DateTime, 30 | Graphics::Events::TDateTimeEvents::DateTimeChanged, 31 | TimeWrapper("2020/06/22 14:24:43", '/', ':')}); 32 | } 33 | 34 | void setupMock() 35 | { 36 | using ::testing::Return; 37 | 38 | EXPECT_CALL(fakePageMock, isVisible()).Times(1).WillOnce(Return(true)); 39 | 40 | EXPECT_CALL(fakePageMock, setHours("14")).Times(1); 41 | EXPECT_CALL(fakePageMock, setMinutes("24")).Times(1); 42 | EXPECT_CALL(fakePageMock, setSeconds("43")).Times(1); 43 | EXPECT_CALL(fakePageMock, setWeekday(std::string_view("MON"))).Times(1); 44 | EXPECT_CALL(fakePageMock, setFullDate("JUN/22/2020")).Times(1); 45 | 46 | pageMockWatchHandler->handleEvent( 47 | {Graphics::Events::EventGroup::DateTime, 48 | Graphics::Events::TDateTimeEvents::DateTimeChanged, 49 | TimeWrapper("2020/06/22 14:24:43", '/', ':')}); 50 | } 51 | 52 | protected: 53 | FakeClockPageMock fakePageMock; 54 | FakeClockPage fakeView{Graphics::Views::IClockWatchPage::ClockPageName}; 55 | std::unique_ptr pageWatchHandler; 56 | std::unique_ptr pageMockWatchHandler; 57 | 58 | private: 59 | void setupTimeZoneEnvironment() 60 | { 61 | // https://github.com/ikonopistsev/btdef/blob/4893ca36f5a251147c57ecfa460acfbe717f69c3/util/time_zone.hpp 62 | // https://science.ksc.nasa.gov/software/winvn/userguide/3_1_4.htm 63 | #ifdef WIN32 64 | _putenv_s("TZ", "UTC"); 65 | _tzset(); 66 | #else 67 | putenv("TZ=UTC"); 68 | tzset(); 69 | #endif 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /Firmware/firmware_tests/coroutine/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(Windows) Запустить", 9 | "type": "cppvsdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/build/coroutine_experiments_app.exe", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": false 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /Firmware/firmware_tests/coroutine/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", 3 | "files.associations": { 4 | "iostream": "cpp" 5 | } 6 | } -------------------------------------------------------------------------------- /Firmware/firmware_tests/coroutine/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(coroutine_experiments C CXX) 4 | 5 | if( UNIX ) 6 | find_package(Threads REQUIRED) 7 | endif() 8 | 9 | add_executable(coroutine_experiments_app ${PROJECT_SOURCES}) 10 | 11 | target_sources( 12 | coroutine_experiments_app 13 | PRIVATE 14 | coroutine_thoughts.cpp 15 | thoughts.hpp 16 | st7789_draft.hpp 17 | ) 18 | 19 | target_compile_features( 20 | coroutine_experiments_app 21 | PRIVATE 22 | cxx_std_20 23 | ) 24 | 25 | if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 26 | set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /fsanitize=address") 27 | else() 28 | set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") 29 | set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") 30 | set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") 31 | endif() 32 | 33 | target_link_libraries( 34 | coroutine_experiments_app 35 | PRIVATE 36 | drivers_ih 37 | watch_display 38 | UtilsLibrary 39 | logger_service 40 | ) 41 | if(UNIX) 42 | target_link_libraries( coroutine_experiments_app PUBLIC Threads::Threads) 43 | endif() 44 | -------------------------------------------------------------------------------- /Firmware/firmware_tests/coroutine/coroutine_thoughts.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #include "st7789_draft.hpp" 20 | 21 | CoroUtils::Task coroutineTask() 22 | { 23 | int b = 32; 24 | 25 | co_return b; 26 | } 27 | 28 | void showFlashDeviceId() 29 | { 30 | CoroUtils::Task task = coroutineTask(); 31 | int testValue = co_await task; 32 | } 33 | 34 | int main() 35 | { 36 | using TSpiBus = Interface::SpiTemplated::SpiBus; 37 | 38 | using TDisplayDriver = DisplayDriver::GC9A01Compact; 39 | /*Display display{}; 40 | display.fillRectangle(0, 0, 220, 220, nullptr);*/ 41 | 42 | TDisplayDriver compactGc9A01; 43 | compactGc9A01.fillRectangle(0, 0, 0, 0, nullptr); 44 | compactGc9A01.initialize(); 45 | // ST7789Coroutine displayCoro{ 46 | // Interface::Spi::createSpiBusAsync() 47 | // , 240 48 | // , 240 49 | //}; 50 | // displayCoro.fillRectangle(0, 0, 0, 0, nullptr); 51 | 52 | showFlashDeviceId(); 53 | 54 | while (true) 55 | { 56 | using namespace std::chrono_literals; 57 | std::this_thread::sleep_for(100ms); 58 | CoroUtils::CoroQueueMainLoop::GetInstance().processQueue(); 59 | } 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /Firmware/firmware_tests/drivers/spi/mock_gpio.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace Gpio 7 | { 8 | 9 | class MockGpio 10 | { 11 | public: 12 | 13 | MockGpio() = default; 14 | 15 | ~MockGpio() = default; 16 | 17 | public: 18 | MOCK_METHOD(void, setGpioHigh, ()); 19 | MOCK_METHOD(void, setGpioLow, ()); 20 | 21 | private: 22 | GTEST_DISALLOW_COPY_AND_ASSIGN_(MockGpio); 23 | }; 24 | 25 | }; -------------------------------------------------------------------------------- /Firmware/firmware_tests/drivers/spi/mock_spi.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace SpiMock 4 | { 5 | 6 | class SpiMocker 7 | { 8 | public: 9 | SpiMocker() = default; 10 | 11 | ~SpiMocker() = default; 12 | 13 | public: 14 | MOCK_METHOD(std::span, receivedData, ()); 15 | MOCK_METHOD(void, sentData, (std::span)); 16 | 17 | private: 18 | GTEST_DISALLOW_COPY_AND_ASSIGN_(SpiMocker); 19 | }; 20 | 21 | }; // namespace Gpio -------------------------------------------------------------------------------- /Firmware/firmware_tests/drivers/windond_flash/flash_fixture.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | // TODO Fix include more clearly 14 | #include "../spi/spi_fake_backend.hpp" 15 | #include "../spi/mock_gpio.hpp" 16 | 17 | class FlashDriverTest : public ::testing::Test 18 | { 19 | 20 | public: 21 | using TFlashTestDriver = ExternalFlash::WinbondFlashDriver< 22 | Interface::SpiTemplated::SpiBus>; 23 | using TDataStream = std::vector; 24 | 25 | protected: 26 | void SetUp() override 27 | { 28 | } 29 | 30 | decltype(auto) getMockGpio() 31 | { 32 | return flashDriver.getSpiBus()->getBackendImpl().accesToCsPin(); 33 | } 34 | 35 | decltype(auto) spiMockAccess() 36 | { 37 | return flashDriver.getSpiBus()->getBackendImpl().accessToSpiMock(); 38 | } 39 | 40 | TDataStream TransactionsToDataStream() 41 | { 42 | return flashDriver.getSpiBus()->getBackendImpl().getTransmittedData(); 43 | } 44 | 45 | protected: 46 | TFlashTestDriver flashDriver; 47 | }; 48 | -------------------------------------------------------------------------------- /Firmware/firmware_tests/main_window/mainwindow_fixture.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "ih/drivers/ih_button_driver.hpp" 5 | 6 | #include "ih/gs_events.hpp" 7 | #include "ih/gs_ievent_handler.hpp" 8 | 9 | #include "ih/gs_imain_window.hpp" 10 | 11 | #include "gs_event_dispatcher.hpp" 12 | 13 | #include "pages_stub_pages_creator.hpp" 14 | #include "widgets_stub_pages_creator.hpp" 15 | 16 | #include "mainwindow_fakes.hpp" 17 | #include "widgets_layer/gs_main_window.hpp" 18 | 19 | #include "ih/pages/gs_iclock_page_view.hpp" 20 | 21 | #include 22 | 23 | class MainWindowTest : public ::testing::Test 24 | { 25 | 26 | protected: 27 | void SetUp() override 28 | { 29 | auto pMainWindowView = Graphics::StubMainWindow::createFakeMainWindowView(); 30 | auto pStubWidgetsCreator = Graphics::StubWidgets::createStubWidgetsCreator(); 31 | auto pStubPagesCreator = Graphics::StubViews::createStubPagesCreator(); 32 | 33 | m_pMainWindow = Graphics::MainWindow::createMainWindow( 34 | std::move(pMainWindowView), 35 | std::move(pStubWidgetsCreator), 36 | std::move(pStubPagesCreator)); 37 | 38 | m_pMainWindow->setPageActive(Graphics::Views::IClockWatchPage::ClockPageName); 39 | } 40 | 41 | protected: 42 | std::unique_ptr m_pMainWindow; 43 | }; -------------------------------------------------------------------------------- /Firmware/firmware_tests/main_window/mainwindow_model_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "mainwindow_fakes.hpp" 5 | #include "mainwindow_fixture.hpp" 6 | 7 | #include "ih/pages/gs_iclock_page_view.hpp" 8 | #include "ih/pages/gs_ihealth_page_view.hpp" 9 | #include "ih/pages/gs_iplayer_page_view.hpp" 10 | 11 | TEST_F(MainWindowTest, InitialSetupMainWindow) 12 | { 13 | EXPECT_EQ( 14 | m_pMainWindow->getActivePage().getPageName(), 15 | Graphics::Views::IClockWatchPage::ClockPageName); 16 | } 17 | 18 | TEST_F(MainWindowTest, ChangeActivePage) 19 | { 20 | m_pMainWindow->setPageActive(Graphics::Views::IHealthWatchPage::HealthPageName); 21 | 22 | EXPECT_EQ( 23 | m_pMainWindow->getActivePage().getPageName(), 24 | Graphics::Views::IHealthWatchPage::HealthPageName); 25 | } 26 | 27 | TEST_F(MainWindowTest, HandleNavigateToNextPage_ButtonClickEvent) 28 | { 29 | m_pMainWindow->getEventDispatcher().postEvent( 30 | {Graphics::Events::EventGroup::Buttons, 31 | Graphics::Events::TButtonsEvents::ButtonClicked, 32 | Graphics::Events::HardwareButtonId::kLeftButtonTop}); 33 | 34 | m_pMainWindow->getEventDispatcher().processEventQueue(); 35 | 36 | EXPECT_EQ( 37 | m_pMainWindow->getActivePage().getPageName(), 38 | Graphics::Views::IHealthWatchPage::HealthPageName); 39 | } 40 | 41 | TEST_F(MainWindowTest, HandleNavigateNextFromLastPage_ButtonClickEvent) 42 | { 43 | m_pMainWindow->setPageActive(Graphics::Views::IPlayerWatchPage::PlayerPageName); 44 | 45 | m_pMainWindow->getEventDispatcher().postEvent( 46 | {Graphics::Events::EventGroup::Buttons, 47 | Graphics::Events::TButtonsEvents::ButtonClicked, 48 | Graphics::Events::HardwareButtonId::kLeftButtonTop}); 49 | 50 | m_pMainWindow->getEventDispatcher().processEventQueue(); 51 | 52 | EXPECT_EQ( 53 | m_pMainWindow->getActivePage().getPageName(), 54 | Graphics::Views::IClockWatchPage::ClockPageName); 55 | } 56 | 57 | TEST_F(MainWindowTest, HandleNavigateToPreviousPageFromInitialState_ButtonClickEvent) 58 | { 59 | m_pMainWindow->getEventDispatcher().postEvent( 60 | {Graphics::Events::EventGroup::Buttons, 61 | Graphics::Events::TButtonsEvents::ButtonClicked, 62 | Graphics::Events::HardwareButtonId::kLeftButtonBottom}); 63 | 64 | m_pMainWindow->getEventDispatcher().processEventQueue(); 65 | 66 | EXPECT_EQ( 67 | m_pMainWindow->getActivePage().getPageName(), 68 | Graphics::Views::IPlayerWatchPage::PlayerPageName); 69 | } -------------------------------------------------------------------------------- /Firmware/firmware_tests/stubs/base_mocked_page.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "ih/gs_ipage_view_object.hpp" 6 | 7 | #include "ih/gs_itheme_controller.hpp" 8 | #include "ih/gs_iwidget_object.hpp" 9 | 10 | #include 11 | 12 | template class FakeBasePage : public PageBaseFake 13 | { 14 | 15 | public: 16 | FakeBasePage(std::string_view _pageName) : m_isVisible{false}, m_pageName{_pageName} 17 | { 18 | } 19 | 20 | void addWidget(Graphics::Widgets::IWidgetObject* _pWidget) noexcept override 21 | { 22 | } 23 | 24 | bool isVisible() const noexcept override 25 | { 26 | return m_isVisible; 27 | } 28 | 29 | void reloadStyle() noexcept override 30 | { 31 | } 32 | 33 | void show() noexcept override 34 | { 35 | m_isVisible = true; 36 | } 37 | void hide() noexcept override 38 | { 39 | m_isVisible = false; 40 | } 41 | 42 | std::string_view getPageName() const noexcept override 43 | { 44 | return m_pageName; 45 | } 46 | 47 | public: 48 | const Graphics::Theme::IThemeController* getThemeController() const noexcept override 49 | { 50 | return nullptr; 51 | } 52 | 53 | private: 54 | bool m_isVisible; 55 | std::string_view m_pageName; 56 | }; 57 | 58 | template class FakeBasePageMock : public PageBaseFake 59 | { 60 | 61 | public: 62 | MOCK_METHOD(void, addWidget, (Graphics::Widgets::IWidgetObject*), (noexcept)); 63 | MOCK_METHOD(bool, isVisible, (), (const, noexcept)); 64 | 65 | void reloadStyle() noexcept override 66 | { 67 | } 68 | 69 | void show() noexcept override 70 | { 71 | } 72 | void hide() noexcept override 73 | { 74 | } 75 | 76 | public: 77 | MOCK_CONST_METHOD0(getPageName, std::string_view()); 78 | MOCK_CONST_METHOD0(getThemeController, const Graphics::Theme::IThemeController*()); 79 | }; -------------------------------------------------------------------------------- /Firmware/firmware_tests/stubs/base_stub_widget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_itheme_controller.hpp" 4 | #include "ih/gs_iwidget_object.hpp" 5 | 6 | #include 7 | 8 | template class FakeBaseWidget : public BaseWidgetStub 9 | { 10 | 11 | public: 12 | void reloadStyle() override 13 | { 14 | } 15 | 16 | void show() override 17 | { 18 | m_isVisible = true; 19 | } 20 | void hide() override 21 | { 22 | m_isVisible = false; 23 | } 24 | 25 | bool isVisible() const override 26 | { 27 | return m_isVisible; 28 | } 29 | 30 | const Graphics::Theme::IThemeController* getThemeController() const override 31 | { 32 | return nullptr; 33 | } 34 | 35 | private: 36 | bool m_isVisible = false; 37 | }; -------------------------------------------------------------------------------- /Firmware/firmware_tests/stubs/interfaces/spi/mocked_spi.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Mocked::Interface 4 | { 5 | 6 | } // namespace Mocked::Interface -------------------------------------------------------------------------------- /Firmware/firmware_tests/stubs/pages/clock_watch_fake_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "base_mocked_page.hpp" 6 | 7 | #include "ih/pages/gs_iclock_page_view.hpp" 8 | 9 | class FakeClockPage : public FakeBasePage 10 | { 11 | 12 | public: 13 | FakeClockPage(std::string_view _pageName) noexcept 14 | : FakeBasePage{_pageName} 15 | , m_hoursValue{"00"} 16 | , m_minutesValue{"00"} 17 | , m_secondsValue{":00"} 18 | , m_weekdayValue{"........."} 19 | , m_fulldateValue{"../../...."} 20 | { 21 | } 22 | 23 | public: 24 | void setHours(const std::string& _newHoursValue) noexcept override 25 | { 26 | m_hoursValue = _newHoursValue; 27 | }; 28 | 29 | void setMinutes(const std::string& _newMinutesValue) noexcept override 30 | { 31 | m_minutesValue = _newMinutesValue; 32 | }; 33 | 34 | void setSeconds(const std::string& _newSecondsValue) noexcept override 35 | { 36 | m_secondsValue = _newSecondsValue; 37 | }; 38 | 39 | void setWeekday(std::string_view _newWeekDay) noexcept override 40 | { 41 | m_weekdayValue = _newWeekDay; 42 | }; 43 | 44 | void setFullDate(const std::string& _fullDate) noexcept override 45 | { 46 | m_fulldateValue = _fullDate; 47 | }; 48 | 49 | public: 50 | std::string_view getHours() const noexcept 51 | { 52 | return m_hoursValue; 53 | } 54 | 55 | std::string_view getMinutes() const noexcept 56 | { 57 | return m_minutesValue; 58 | } 59 | 60 | std::string_view getSeconds() const noexcept 61 | { 62 | return m_secondsValue; 63 | } 64 | 65 | std::string_view getWeekday() const noexcept 66 | { 67 | return m_weekdayValue; 68 | } 69 | 70 | std::string_view getFullDate() const noexcept 71 | { 72 | return m_fulldateValue; 73 | } 74 | 75 | private: 76 | std::string m_hoursValue; 77 | std::string m_minutesValue; 78 | std::string m_secondsValue; 79 | std::string m_weekdayValue; 80 | std::string m_fulldateValue; 81 | }; 82 | 83 | class FakeClockPageMock : public FakeBasePageMock 84 | { 85 | 86 | public: 87 | MOCK_METHOD(void, setHours, (const std::string&), (noexcept, override)); 88 | MOCK_METHOD(void, setMinutes, (const std::string&), (noexcept, override)); 89 | MOCK_METHOD(void, setSeconds, (const std::string&), (noexcept, override)); 90 | MOCK_METHOD(void, setWeekday, (std::string_view), (noexcept, override)); 91 | MOCK_METHOD(void, setFullDate, (const std::string&), (noexcept, override)); 92 | }; -------------------------------------------------------------------------------- /Firmware/firmware_tests/stubs/pages/health_watch_fake_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "base_mocked_page.hpp" 6 | 7 | #include "ih/pages/gs_ihealth_page_view.hpp" 8 | 9 | class FakeHealthPage : public FakeBasePage 10 | { 11 | 12 | public: 13 | FakeHealthPage(std::string_view _pageName) noexcept : FakeBasePage{_pageName} 14 | { 15 | } 16 | 17 | public: 18 | void setStepsCount(std::uint8_t _newStepsValue) noexcept override 19 | { 20 | } 21 | 22 | void setHeartrate(std::uint8_t _newHeartrateValue) noexcept override 23 | { 24 | } 25 | 26 | void setCalloriesCount(std::uint8_t _newCalloriesCount) noexcept override 27 | { 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /Firmware/firmware_tests/stubs/pages/player_watch_fake_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "base_mocked_page.hpp" 6 | 7 | #include "ih/pages/gs_iplayer_page_view.hpp" 8 | 9 | class FakePlayerPage : public FakeBasePage 10 | { 11 | 12 | public: 13 | FakePlayerPage(std::string_view _pageName) noexcept : FakeBasePage{_pageName} 14 | { 15 | } 16 | 17 | public: 18 | void setPause() noexcept override 19 | { 20 | } 21 | 22 | void setPlaying() noexcept override 23 | { 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /Firmware/firmware_tests/stubs/pages_stub_pages_creator.cpp: -------------------------------------------------------------------------------- 1 | #include "pages_stub_pages_creator.hpp" 2 | 3 | #include "pages/clock_watch_fake_view.hpp" 4 | #include "pages/health_watch_fake_view.hpp" 5 | #include "pages/player_watch_fake_view.hpp" 6 | 7 | namespace Graphics::StubViews 8 | { 9 | 10 | StubPagesCreator::~StubPagesCreator() = default; 11 | 12 | std::unique_ptr StubPagesCreator::createClockPage( 13 | Graphics::Theme::IThemeController* _pThemeController) noexcept 14 | { 15 | return std::make_unique(Graphics::Views::IClockWatchPage::ClockPageName); 16 | } 17 | 18 | std::unique_ptr StubPagesCreator::createHealthPage( 19 | Graphics::Theme::IThemeController* _pThemeController) noexcept 20 | { 21 | return std::make_unique(Graphics::Views::IHealthWatchPage::HealthPageName); 22 | } 23 | 24 | std::unique_ptr StubPagesCreator::createPlayerPage( 25 | Graphics::Theme::IThemeController* _pThemeController) noexcept 26 | { 27 | return std::make_unique(Graphics::Views::IPlayerWatchPage::PlayerPageName); 28 | } 29 | 30 | Graphics::Views::IPagesCreator::Ptr createStubPagesCreator() noexcept 31 | { 32 | return std::make_unique(); 33 | } 34 | 35 | } // namespace Graphics::StubViews -------------------------------------------------------------------------------- /Firmware/firmware_tests/stubs/pages_stub_pages_creator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/creators/gs_ipages_creator.hpp" 4 | 5 | namespace Graphics::StubViews 6 | { 7 | 8 | class StubPagesCreator : public Graphics::Views::IPagesCreator 9 | { 10 | public: 11 | ~StubPagesCreator() override; 12 | 13 | public: 14 | std::unique_ptr createClockPage( 15 | Graphics::Theme::IThemeController* _pThemeController) noexcept override; 16 | 17 | std::unique_ptr createHealthPage( 18 | Graphics::Theme::IThemeController* _pThemeController) noexcept override; 19 | 20 | std::unique_ptr createPlayerPage( 21 | Graphics::Theme::IThemeController* _pThemeController) noexcept override; 22 | }; 23 | 24 | Graphics::Views::IPagesCreator::Ptr createStubPagesCreator() noexcept; 25 | 26 | } // namespace Graphics::StubViews -------------------------------------------------------------------------------- /Firmware/firmware_tests/stubs/widgets/battery_widget_fake.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base_stub_widget.hpp" 4 | 5 | #include "ih/widgets/gs_ibattery_widget.hpp" 6 | 7 | class BatteryWidgetStub : public FakeBaseWidget 8 | { 9 | public: 10 | void setBatteryLevelPercentage(const std::uint8_t _newBatteryLevel) override 11 | { 12 | } 13 | void setBatteryStatus(Graphics::Widgets::IBatteryWidget::BatteryStatus _iconToSet) override 14 | { 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /Firmware/firmware_tests/stubs/widgets/bluetooth_widget_fake.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base_stub_widget.hpp" 4 | 5 | #include "ih/widgets/gs_ibluetooth_widget.hpp" 6 | 7 | class BluetoothWidgetStub : public FakeBaseWidget 8 | { 9 | public: 10 | void setBluetoothStatus( 11 | Graphics::Widgets::IBluetoothWidget::BluetoothStatus _iconToSet) override 12 | { 13 | } 14 | }; -------------------------------------------------------------------------------- /Firmware/firmware_tests/stubs/widgets/pages_switch_widget_fake.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "base_stub_widget.hpp" 4 | 5 | #include "ih/widgets/gs_ipages_switch.hpp" 6 | 7 | class PagesSwitchWidgetStub : public FakeBaseWidget 8 | { 9 | public: 10 | void setActivePage(std::string_view _pageName) override 11 | { 12 | } 13 | }; -------------------------------------------------------------------------------- /Firmware/firmware_tests/stubs/widgets_stub_pages_creator.cpp: -------------------------------------------------------------------------------- 1 | #include "widgets_stub_pages_creator.hpp" 2 | 3 | #include "battery_widget_fake.hpp" 4 | #include "bluetooth_widget_fake.hpp" 5 | #include "pages_switch_widget_fake.hpp" 6 | 7 | namespace Graphics::StubWidgets 8 | { 9 | StubWidgetsCreator::~StubWidgetsCreator() = default; 10 | 11 | std::unique_ptr StubWidgetsCreator::createBluetoothWidget( 12 | Graphics::Theme::IThemeController* _pThemeController) noexcept 13 | { 14 | return std::make_unique(); 15 | } 16 | std::unique_ptr StubWidgetsCreator::createPagesSwitchWidget( 17 | Graphics::Theme::IThemeController* _pThemeController) noexcept 18 | { 19 | return std::make_unique(); 20 | } 21 | std::unique_ptr StubWidgetsCreator::createBatteryWidget( 22 | Graphics::Theme::IThemeController* _pThemeController) noexcept 23 | { 24 | return std::make_unique(); 25 | } 26 | Graphics::Widgets::IWidgetsCreator::Ptr createStubWidgetsCreator() noexcept 27 | { 28 | return std::make_unique(); 29 | } 30 | } // namespace Graphics::StubWidgets -------------------------------------------------------------------------------- /Firmware/firmware_tests/stubs/widgets_stub_pages_creator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/creators/gs_iwidgets_creator.hpp" 4 | 5 | namespace Graphics::StubWidgets 6 | { 7 | 8 | class StubWidgetsCreator : public Graphics::Widgets::IWidgetsCreator 9 | { 10 | public: 11 | ~StubWidgetsCreator() override; 12 | 13 | public: 14 | std::unique_ptr createBluetoothWidget( 15 | Graphics::Theme::IThemeController* _pThemeController) noexcept override; 16 | 17 | std::unique_ptr createPagesSwitchWidget( 18 | Graphics::Theme::IThemeController* _pThemeController) noexcept override; 19 | 20 | std::unique_ptr createBatteryWidget( 21 | Graphics::Theme::IThemeController* _pThemeController) noexcept override; 22 | }; 23 | 24 | Graphics::Widgets::IWidgetsCreator::Ptr createStubWidgetsCreator() noexcept; 25 | 26 | } // namespace Graphics::StubWidgets -------------------------------------------------------------------------------- /Firmware/graphics/fonts/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | add_library(lvgl_fonts STATIC) 3 | 4 | 5 | target_sources( 6 | lvgl_fonts 7 | PRIVATE 8 | ${CMAKE_CURRENT_LIST_DIR}/LcdNova12px.cpp 9 | ${CMAKE_CURRENT_LIST_DIR}/LcdNova16px.cpp 10 | ${CMAKE_CURRENT_LIST_DIR}/LcdNova24px.cpp 11 | ${CMAKE_CURRENT_LIST_DIR}/LcdNova30px.cpp 12 | ${CMAKE_CURRENT_LIST_DIR}/LcdNova36px.cpp 13 | ${CMAKE_CURRENT_LIST_DIR}/LcdNova68px.cpp 14 | ${CMAKE_CURRENT_LIST_DIR}/IconFont16px.cpp 15 | ${CMAKE_CURRENT_LIST_DIR}/IconFont24px.cpp 16 | ${CMAKE_CURRENT_LIST_DIR}/IconFont35px.cpp 17 | ${CMAKE_CURRENT_LIST_DIR}/PlayerIcons68px.cpp 18 | ) 19 | target_link_libraries(lvgl_fonts PUBLIC lvgl_lib) -------------------------------------------------------------------------------- /Firmware/graphics/fonts/GeneratedFontello/IconFont16px.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Firmware/graphics/fonts/GeneratedFontello/IconFont16px.ttf -------------------------------------------------------------------------------- /Firmware/graphics/fonts/GeneratedFontello/IconFont24px.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Firmware/graphics/fonts/GeneratedFontello/IconFont24px.ttf -------------------------------------------------------------------------------- /Firmware/graphics/fonts/GeneratedFontello/IconFont35px.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Firmware/graphics/fonts/GeneratedFontello/IconFont35px.ttf -------------------------------------------------------------------------------- /Firmware/graphics/fonts/GeneratedFontello/PlayerIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Firmware/graphics/fonts/GeneratedFontello/PlayerIcons.ttf -------------------------------------------------------------------------------- /Firmware/graphics/fonts/GeneratedFontello/PlayerIconsCodes.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Firmware/graphics/fonts/GeneratedFontello/PlayerIconsCodes.JPG -------------------------------------------------------------------------------- /Firmware/graphics/fonts/GeneratedFontello/a_LCDNova.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Firmware/graphics/fonts/GeneratedFontello/a_LCDNova.ttf -------------------------------------------------------------------------------- /Firmware/graphics/fonts/IconFont16px.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace IconFontSymbols::Battery 5 | { 6 | const std::string_view Battery25Percent = "\xEF\x89\x80"; 7 | const std::string_view Battery50Percent = "\xEF\x89\x81"; 8 | const std::string_view Battery75Percent = "\xEF\x89\x82"; 9 | const std::string_view Battery90Percent = "\xEF\x89\x83"; 10 | const std::string_view BatteryCharged = "\xEF\x89\x84"; 11 | } 12 | 13 | namespace IconFontSymbols::Bluetooth 14 | { 15 | const std::string_view BluetoothEnabled = "\xEF\x89\x85"; 16 | } 17 | 18 | namespace IconFontSymbols::Health 19 | { 20 | const std::string_view HeartIcon = "\xEF\x89\x86"; 21 | } -------------------------------------------------------------------------------- /Firmware/graphics/fonts/IconFont24px.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | 5 | namespace IconFontSymbols::Music 6 | { 7 | const std::string_view Melody = "\xEE\xA0\x81"; 8 | } -------------------------------------------------------------------------------- /Firmware/graphics/fonts/IconFont35px.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace IconFontSymbols::Steps 5 | { 6 | const std::string_view StepsIcon = "\xEE\xA0\x82"; 7 | } -------------------------------------------------------------------------------- /Firmware/graphics/fonts/PlayerIcons68px.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace IconFontSymbols::Player 6 | { 7 | const std::string_view FirstPart = "\xEE\xA0\x81"; 8 | const std::string_view SecondPart = "\xEE\xA0\x80"; 9 | 10 | const std::string_view ArrowLeft = "\xEE\xA0\x82"; 11 | const std::string_view ArrowRight = "\xEE\xA0\x83"; 12 | const std::string_view Line = "\xEE\xA0\x84"; 13 | } 14 | -------------------------------------------------------------------------------- /Firmware/graphics/gs_event_dispatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "gs_event_dispatcher.hpp" 2 | 3 | #include 4 | 5 | namespace Graphics::Events 6 | { 7 | 8 | void EventDispatcher::subscribe(EventGroup _eventGroup, const TEventHandler& _handler) noexcept 9 | { 10 | auto it = 11 | std::find_if(m_eventsMap.begin(), m_eventsMap.end(), [_eventGroup](const auto& _toCompare) { 12 | const auto& [event, storage] = _toCompare; 13 | return _eventGroup == event; 14 | }); 15 | 16 | if (it != m_eventsMap.end()) 17 | { 18 | it->second.emplace_back(_handler); 19 | } 20 | else 21 | { 22 | m_eventsMap.push_back({_eventGroup, {_handler}}); 23 | } 24 | } 25 | 26 | void EventDispatcher::postEvent(TEvent&& _eventToProcess) noexcept 27 | { 28 | m_eventsQueue.push(std::move(_eventToProcess)); 29 | } 30 | 31 | void EventDispatcher::processEventQueue() noexcept 32 | { 33 | TEvent tempEvent{}; 34 | while (m_eventsQueue.pop(tempEvent)) 35 | { 36 | auto it = std::find_if( 37 | m_eventsMap.begin(), 38 | m_eventsMap.end(), 39 | [eventGroup = tempEvent.eventGroup](const auto& _toCompare) { 40 | const auto& [event, storage] = _toCompare; 41 | return eventGroup == event; 42 | }); 43 | if (it != m_eventsMap.end()) 44 | { 45 | std::for_each( 46 | it->second.cbegin(), it->second.cend(), [tempEvent](const auto& _callback) { 47 | _callback(tempEvent); 48 | }); 49 | } 50 | } 51 | } 52 | 53 | std::unique_ptr createEventDispatcher() noexcept 54 | { 55 | return std::make_unique(); 56 | } 57 | 58 | } // namespace Graphics::Events 59 | -------------------------------------------------------------------------------- /Firmware/graphics/gs_event_dispatcher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_events.hpp" 4 | 5 | #include "utils/Noncopyable.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | namespace Graphics::Events 17 | { 18 | 19 | class EventDispatcher : private Utils::noncopyable 20 | { 21 | 22 | public: 23 | using TEventHandler = std::function; 24 | 25 | static constexpr inline int MaxSubscriberLimit = 3; 26 | using SubscriberStorage = etl::vector; 27 | 28 | void subscribe(EventGroup _eventGroup, const TEventHandler& _handler) noexcept; 29 | 30 | void postEvent(TEvent&& _eventToProcess) noexcept; 31 | 32 | void processEventQueue() noexcept; 33 | 34 | private: 35 | static constexpr inline int EventsCount = 36 | Events::enumConvert(EventGroup::EventGroupEnd); 37 | static constexpr inline int EventPoolSize = 16; 38 | 39 | using TEventsMap = etl::vector, EventsCount>; 40 | TEventsMap m_eventsMap; 41 | 42 | using TEventsQueue = 43 | etl::queue_spsc_atomic; 44 | TEventsQueue m_eventsQueue; 45 | }; 46 | 47 | using TEventDispatcherPtr = std::unique_ptr; 48 | 49 | TEventDispatcherPtr createEventDispatcher() noexcept; 50 | 51 | } // namespace Graphics::Events 52 | -------------------------------------------------------------------------------- /Firmware/graphics/gs_lvgl_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils/MetaUtils.hpp" 4 | #include 5 | 6 | namespace Graphics 7 | { 8 | class PlatformBackend; 9 | }; 10 | 11 | namespace Graphics::MainWindow 12 | { 13 | class IGsMainWindowModel; 14 | }; 15 | 16 | namespace Graphics 17 | { 18 | 19 | class LvglGraphicsService 20 | { 21 | 22 | public: 23 | explicit LvglGraphicsService() noexcept; 24 | 25 | ~LvglGraphicsService() noexcept; 26 | 27 | void executeGlTask() noexcept; 28 | 29 | public: 30 | Graphics::MainWindow::IGsMainWindowModel& getMainWindow() noexcept; 31 | 32 | Graphics::MainWindow::IGsMainWindowModel& getMainWindow() const noexcept; 33 | 34 | private: 35 | void initMainWindow() noexcept; 36 | 37 | private: 38 | class GSLvglServiceImpl; 39 | std::unique_ptr m_pGraphicsServiceImpl; 40 | }; 41 | 42 | std::unique_ptr createGraphicsService() noexcept; 43 | 44 | }; // namespace Graphics 45 | -------------------------------------------------------------------------------- /Firmware/graphics/gs_theme_controller.hpp: -------------------------------------------------------------------------------- 1 | #include "ih/gs_itheme_controller.hpp" 2 | 3 | #include 4 | 5 | namespace Graphics::Theme 6 | { 7 | 8 | class ThemeController : public IThemeController 9 | { 10 | 11 | public: 12 | explicit ThemeController( 13 | const ColorTheme _initialColorTheme, 14 | std::uint32_t _displayWidth, 15 | std::uint32_t _displayHeight) noexcept; 16 | 17 | void setActiveTheme(ColorTheme _themeToSet) noexcept override; 18 | 19 | ColorTheme getActiveTheme() const noexcept override; 20 | 21 | public: 22 | lv_style_t getIconsFont(FontSize _fontStyle, Color _fontColor) const noexcept override; 23 | 24 | lv_style_t getFontStyle(FontSize _fontStyle, Color _fontColor) const noexcept override; 25 | 26 | lv_color_t getMainThemeColor(Color _fontColor) const noexcept override; 27 | 28 | std::uint32_t getDisplayWidth() const noexcept override; 29 | 30 | std::uint32_t getDisplayHeight() const noexcept override; 31 | 32 | public: 33 | ~ThemeController() override = default; 34 | 35 | private: 36 | void initColorsAccrodingToTheme(ColorTheme _theme) noexcept; 37 | 38 | private: 39 | ColorTheme m_activeTheme; 40 | 41 | const std::uint32_t m_displayWidth; 42 | const std::uint32_t m_displayHeight; 43 | 44 | lv_color_t m_mainThemeDarkColor; 45 | lv_color_t m_mainThemeLightColor; 46 | }; 47 | 48 | std::unique_ptr createThemeController( 49 | const ColorTheme _initialColorTheme, 50 | std::uint32_t _displayWidth, 51 | std::uint32_t _displayHeight) noexcept; 52 | 53 | } // namespace Graphics::Theme -------------------------------------------------------------------------------- /Firmware/graphics/icons/fontello.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Firmware/graphics/icons/fontello.ttf -------------------------------------------------------------------------------- /Firmware/graphics/ih/creators/gs_ipages_creator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_ipage_view_object.hpp" 4 | 5 | #include 6 | 7 | namespace Graphics::Views 8 | { 9 | class IClockWatchPage; 10 | class IHealthWatchPage; 11 | class IPlayerWatchPage; 12 | } // namespace Graphics::Views 13 | 14 | namespace Graphics::Views 15 | { 16 | class IPagesCreator 17 | { 18 | public: 19 | using Ptr = std::unique_ptr; 20 | 21 | [[nodiscard]] virtual std::unique_ptr createClockPage( 22 | Theme::IThemeController* _pThemeController) noexcept = 0; 23 | 24 | [[nodiscard]] virtual std::unique_ptr createHealthPage( 25 | Theme::IThemeController* _pThemeController) noexcept = 0; 26 | 27 | [[nodiscard]] virtual std::unique_ptr createPlayerPage( 28 | Theme::IThemeController* _pThemeController) noexcept = 0; 29 | 30 | virtual ~IPagesCreator() = default; 31 | }; 32 | 33 | } // namespace Graphics::Views -------------------------------------------------------------------------------- /Firmware/graphics/ih/creators/gs_iwidgets_creator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_iwidget_object.hpp" 4 | 5 | #include 6 | 7 | namespace Graphics::Widgets 8 | { 9 | class IBluetoothWidget; 10 | class IPagesSwitch; 11 | class IBatteryWidget; 12 | } // namespace Graphics::Widgets 13 | 14 | namespace Graphics::Widgets 15 | { 16 | class IWidgetsCreator 17 | { 18 | public: 19 | using Ptr = std::unique_ptr; 20 | 21 | [[nodiscard]] virtual std::unique_ptr 22 | createBluetoothWidget(Theme::IThemeController* _pThemeController) noexcept = 0; 23 | 24 | [[nodiscard]] virtual std::unique_ptr createPagesSwitchWidget( 25 | Theme::IThemeController* _pThemeController) noexcept = 0; 26 | 27 | [[nodiscard]] virtual std::unique_ptr createBatteryWidget( 28 | Theme::IThemeController* _pThemeController) noexcept = 0; 29 | 30 | virtual ~IWidgetsCreator() = default; 31 | }; 32 | 33 | } // namespace Graphics::Widgets -------------------------------------------------------------------------------- /Firmware/graphics/ih/gs_events.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace Graphics::Events 7 | { 8 | 9 | enum class EventGroup 10 | { 11 | Battery, 12 | Heartrate, 13 | BleDevice, 14 | Gesture, 15 | HardwareButtons, 16 | DateTime, 17 | GraphicsEvents, 18 | Buttons, 19 | EventGroupEnd 20 | }; 21 | 22 | struct TEvent 23 | { 24 | EventGroup eventGroup; 25 | std::any eventType; 26 | std::any eventData; 27 | }; 28 | 29 | enum class TEventKind 30 | { 31 | EventBegin 32 | }; 33 | 34 | template 35 | constexpr auto enumConvert(TToConvert _valueToConvert) 36 | { 37 | return static_cast::type>(_valueToConvert); 38 | } 39 | 40 | enum class TBatteryEvents 41 | { 42 | BatteryEventsStart = enumConvert(TEventKind::EventBegin), 43 | BatteryLevelChanged, 44 | BatteryEventsEnd 45 | }; 46 | 47 | enum class THeartRateEvents 48 | { 49 | HeartRateEventsBegin = enumConvert(TBatteryEvents::BatteryEventsEnd), 50 | MeasureStarted, 51 | MeasureCompleted, 52 | MeasureFailed, 53 | HeartRateEventsEnd 54 | }; 55 | 56 | enum class TDateTimeEvents 57 | { 58 | DateTimeEventsBegin = enumConvert(THeartRateEvents::HeartRateEventsEnd), 59 | DateTimeChanged, 60 | DateTimeEventsEnd 61 | }; 62 | 63 | enum class TBleClientEvents 64 | { 65 | BleClientEventsBegin = enumConvert(TDateTimeEvents::DateTimeEventsEnd), 66 | DeviceConnected, 67 | DeviceDisconnected, 68 | BleClientEventsEnd 69 | }; 70 | 71 | enum class TGraphicsEvents 72 | { 73 | GraphicsEventsBegin = enumConvert(TBleClientEvents::BleClientEventsEnd), 74 | PageHiding, 75 | PageActivated, 76 | GraphicsEventsEnd 77 | }; 78 | 79 | enum class TButtonsEvents 80 | { 81 | TButtonsEventsBegin = enumConvert(TGraphicsEvents::GraphicsEventsEnd), 82 | ButtonClicked, 83 | ButtonPressed, 84 | ButtonReleased, 85 | ButtonDblClick, 86 | ButtonLongClick, 87 | ButtonEventsEnd 88 | }; 89 | 90 | enum class HardwareButtonId 91 | { 92 | kLeftButtonTop, 93 | kLeftButtonMedium, 94 | kLeftButtonBottom, 95 | kRightButtonTop, 96 | kRightButtonBottom 97 | }; 98 | 99 | } // namespace Graphics::Events 100 | -------------------------------------------------------------------------------- /Firmware/graphics/ih/gs_ievent_handler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "gs_events.hpp" 3 | 4 | #include 5 | 6 | namespace Graphics::Events 7 | { 8 | struct TEvent; 9 | } 10 | 11 | namespace Graphics 12 | { 13 | class IEventHandler 14 | { 15 | 16 | public: 17 | virtual ~IEventHandler() = default; 18 | 19 | public: 20 | virtual void handleEvent(const Events::TEvent& _event) noexcept = 0; 21 | }; 22 | 23 | using TEventHandlerPtr = std::unique_ptr; 24 | } // namespace Graphics -------------------------------------------------------------------------------- /Firmware/graphics/ih/gs_imain_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "utils/SimpleSignal.hpp" 6 | 7 | namespace Graphics::Views 8 | { 9 | class IPageViewObject; 10 | } 11 | 12 | namespace Graphics::Events 13 | { 14 | struct TEvent; 15 | class EventDispatcher; 16 | } // namespace Graphics::Events 17 | 18 | namespace Graphics::Theme 19 | { 20 | class IThemeController; 21 | } 22 | 23 | namespace Graphics::MainWindow 24 | { 25 | 26 | class IGsMainWindowModel 27 | { 28 | 29 | public: 30 | virtual ~IGsMainWindowModel() = default; 31 | 32 | public: 33 | virtual void handleEvent(const Events::TEvent& _tEvent) noexcept = 0; 34 | 35 | virtual void addPage(std::unique_ptr&& _toAdd) noexcept = 0; 36 | 37 | virtual void setPageActive(std::string_view _pageName) noexcept = 0; 38 | 39 | virtual Graphics::Views::IPageViewObject& getActivePage() noexcept = 0; 40 | 41 | virtual const Graphics::Views::IPageViewObject& getActivePage() const noexcept = 0; 42 | 43 | virtual const Graphics::Views::IPageViewObject& getPage( 44 | std::string_view _pageName) const noexcept = 0; 45 | 46 | using TPageWalker = std::function; 47 | 48 | virtual void forEachPage(TPageWalker _pageWalker) noexcept = 0; 49 | 50 | public: 51 | virtual void handleEventTimerEllapsed() noexcept = 0; 52 | 53 | virtual Events::EventDispatcher& getEventDispatcher() noexcept = 0; 54 | 55 | virtual const Theme::IThemeController* getThemeController() const noexcept = 0; 56 | 57 | virtual Theme::IThemeController* getThemeController() noexcept = 0; 58 | 59 | public: 60 | Simple::Signal onActivePageChanged; 61 | }; 62 | 63 | class IMainWindowView 64 | { 65 | 66 | public: 67 | virtual ~IMainWindowView() = default; 68 | 69 | public: 70 | virtual void initBackground() noexcept = 0; 71 | 72 | virtual void resetBackgroundStyle() noexcept = 0; 73 | 74 | public: 75 | virtual const Theme::IThemeController* getThemeController() const noexcept = 0; 76 | 77 | virtual Theme::IThemeController* getThemeController() noexcept = 0; 78 | }; 79 | 80 | }; // namespace Graphics::MainWindow 81 | -------------------------------------------------------------------------------- /Firmware/graphics/ih/gs_ipage_view_object.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace Graphics::Widgets 7 | { 8 | class IWidgetObject; 9 | } 10 | 11 | namespace Graphics::Theme 12 | { 13 | class IThemeController; 14 | } 15 | 16 | namespace Graphics::Views 17 | { 18 | 19 | class IEventHandler; 20 | 21 | class IPageViewObject 22 | { 23 | 24 | public: 25 | virtual ~IPageViewObject() = default; 26 | 27 | public: 28 | virtual void addWidget(Graphics::Widgets::IWidgetObject* _pWidget) noexcept = 0; 29 | 30 | virtual void show() noexcept = 0; 31 | 32 | virtual void hide() noexcept = 0; 33 | 34 | virtual bool isVisible() const noexcept = 0; 35 | 36 | virtual void reloadStyle() noexcept = 0; 37 | 38 | public: 39 | virtual std::string_view getPageName() const = 0; 40 | 41 | virtual const Theme::IThemeController* getThemeController() const = 0; 42 | }; 43 | 44 | }; // namespace Graphics::Views 45 | -------------------------------------------------------------------------------- /Firmware/graphics/ih/gs_itheme_controller.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "lvgl.h" 3 | 4 | #include "utils/SimpleSignal.hpp" 5 | 6 | namespace Graphics::Theme 7 | { 8 | enum class ColorTheme 9 | { 10 | Night, 11 | Light, 12 | Pastele 13 | }; 14 | 15 | enum class Color 16 | { 17 | MainThemeLight, 18 | MainThemeDark 19 | }; 20 | // clang-format off 21 | enum class FontSize 22 | { 23 | extra_small // 12px 24 | , small // 16px 25 | , below_normal // 24px 26 | , normal // 30px 27 | , large // 36px 28 | , x_large // 68px 29 | }; 30 | // clang-format on 31 | 32 | class IThemeController 33 | { 34 | 35 | public: 36 | virtual void setActiveTheme(ColorTheme _themeToSet) noexcept = 0; 37 | 38 | virtual ColorTheme getActiveTheme() const noexcept = 0; 39 | 40 | public: 41 | virtual lv_style_t getIconsFont(FontSize _fontStyle, Color _fontColor) const noexcept = 0; 42 | 43 | virtual lv_style_t getFontStyle(FontSize _fontStyle, Color _fontColor) const noexcept = 0; 44 | 45 | virtual lv_color_t getMainThemeColor(Color _fontColor) const noexcept = 0; 46 | 47 | virtual std::uint32_t getDisplayWidth() const noexcept = 0; 48 | 49 | virtual std::uint32_t getDisplayHeight() const noexcept = 0; 50 | 51 | public: 52 | Simple::Signal onThemeChanged; 53 | 54 | public: 55 | virtual ~IThemeController() = default; 56 | }; 57 | } // namespace Graphics::Theme -------------------------------------------------------------------------------- /Firmware/graphics/ih/gs_iwidget_object.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Graphics::Views 6 | { 7 | class IPageViewObject; 8 | }; 9 | 10 | namespace Graphics::Theme 11 | { 12 | class IThemeController; 13 | } 14 | 15 | namespace Graphics::Widgets 16 | { 17 | 18 | class IWidgetObject 19 | { 20 | 21 | public: 22 | virtual ~IWidgetObject() = default; 23 | 24 | public: 25 | virtual void show() = 0; 26 | 27 | virtual void hide() = 0; 28 | 29 | virtual bool isVisible() const = 0; 30 | 31 | virtual void reloadStyle() = 0; 32 | 33 | public: 34 | virtual const Graphics::Theme::IThemeController* getThemeController() const = 0; 35 | }; 36 | 37 | }; // namespace Graphics::Widgets 38 | -------------------------------------------------------------------------------- /Firmware/graphics/ih/pages/gs_iclock_page_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_ipage_view_object.hpp" 4 | 5 | #include 6 | 7 | namespace Graphics::Views 8 | { 9 | 10 | class IClockWatchPage : public IPageViewObject 11 | { 12 | 13 | public: 14 | virtual void setHours(const std::string& _newHoursValue) noexcept = 0; 15 | 16 | virtual void setMinutes(const std::string& _newMinutesValue) noexcept = 0; 17 | 18 | virtual void setSeconds(const std::string& _newSecondsValue) noexcept = 0; 19 | 20 | virtual void setWeekday(std::string_view _newWeekDay) noexcept = 0; 21 | 22 | virtual void setFullDate(const std::string& _fullDate) noexcept = 0; 23 | 24 | public: 25 | static constexpr std::string_view ClockPageName = "ClockPage"; 26 | }; 27 | 28 | } // namespace Graphics::Views 29 | -------------------------------------------------------------------------------- /Firmware/graphics/ih/pages/gs_ihealth_page_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_ipage_view_object.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace Graphics::Views 9 | { 10 | 11 | class IHealthWatchPage : public IPageViewObject 12 | { 13 | 14 | public: 15 | virtual void setStepsCount(std::uint8_t _newStepsValue) noexcept = 0; 16 | 17 | virtual void setHeartrate(std::uint8_t _newHeartrateValue) noexcept = 0; 18 | 19 | virtual void setCalloriesCount(std::uint8_t _newCalloriesCount) noexcept = 0; 20 | 21 | public: 22 | static constexpr std::string_view HealthPageName = "HealthPage"; 23 | }; 24 | 25 | } // namespace Graphics::Views 26 | -------------------------------------------------------------------------------- /Firmware/graphics/ih/pages/gs_iplayer_page_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_ipage_view_object.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace Graphics::Views 9 | { 10 | 11 | class IPlayerWatchPage : public IPageViewObject 12 | { 13 | 14 | public: 15 | virtual void setPause() noexcept = 0; 16 | 17 | virtual void setPlaying() noexcept = 0; 18 | 19 | public: 20 | static constexpr std::string_view PlayerPageName = "PlayerPage"; 21 | }; 22 | 23 | } // namespace Graphics::Views 24 | -------------------------------------------------------------------------------- /Firmware/graphics/ih/widgets/gs_ibattery_widget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_iwidget_object.hpp" 4 | 5 | #include 6 | 7 | namespace Graphics::Widgets 8 | { 9 | 10 | class IBatteryWidget 11 | : public IWidgetObject 12 | { 13 | 14 | public: 15 | 16 | enum class BatteryStatus 17 | { 18 | QuaterCharge 19 | , HalfCharged 20 | , Charge75Percents 21 | , Charged90Percents 22 | , FullCharged 23 | }; 24 | 25 | virtual void setBatteryLevelPercentage( const std::uint8_t _newBatteryLevel ) = 0; 26 | 27 | virtual void setBatteryStatus( BatteryStatus _iconToSet ) = 0; 28 | }; 29 | 30 | }; 31 | -------------------------------------------------------------------------------- /Firmware/graphics/ih/widgets/gs_ibluetooth_widget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_iwidget_object.hpp" 4 | 5 | #include 6 | 7 | namespace Graphics::Widgets 8 | { 9 | 10 | class IBluetoothWidget 11 | : public IWidgetObject 12 | { 13 | 14 | public: 15 | 16 | enum class BluetoothStatus 17 | { 18 | Connected 19 | , Disconnected 20 | }; 21 | 22 | virtual void setBluetoothStatus( BluetoothStatus _iconToSet ) = 0; 23 | }; 24 | 25 | }; 26 | -------------------------------------------------------------------------------- /Firmware/graphics/ih/widgets/gs_ipages_switch.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_iwidget_object.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace Graphics::Widgets 9 | { 10 | 11 | class IPagesSwitch : public IWidgetObject 12 | { 13 | 14 | public: 15 | virtual void setActivePage(std::string_view _pageName) = 0; 16 | }; 17 | 18 | }; // namespace Graphics::Widgets 19 | -------------------------------------------------------------------------------- /Firmware/graphics/lvgl_lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(lvgl_library) 2 | 3 | if( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR" ) 4 | add_subdirectory(lvgl_driver_backend) 5 | endif() -------------------------------------------------------------------------------- /Firmware/graphics/lvgl_lib/GenerateCFilesList.txt: -------------------------------------------------------------------------------- 1 | // Just use $ find . -name "*.c" > allcfiles.txt for generate list of c-files for CMakeLists of lvlgl. -------------------------------------------------------------------------------- /Firmware/graphics/lvgl_lib/lvgl_driver_backend/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | set (TARGET_NAME lvgl_drivers) 4 | 5 | add_library( ${TARGET_NAME} STATIC ) 6 | 7 | set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 8 | set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 9 | set (CMAKE_C_STANDARD 11) 10 | 11 | target_include_directories( 12 | ${TARGET_NAME} 13 | PUBLIC 14 | "lv_drivers/display/" 15 | "lv_drivers/indev/" 16 | "lv_drivers/" 17 | ${CMAKE_CURRENT_LIST_DIR} 18 | ) 19 | 20 | set ( TARGET_SOURCE_LIST 21 | "lv_drivers/display/ILI9341.c" 22 | "lv_drivers/display/UC1610.c" 23 | "lv_drivers/display/GC9A01.c" 24 | "lv_drivers/display/SSD1963.c" 25 | "lv_drivers/display/drm.c" 26 | "lv_drivers/display/fbdev.c" 27 | "lv_drivers/display/SHARP_MIP.c" 28 | "lv_drivers/display/ST7565.c" 29 | "lv_drivers/display/R61581.c" 30 | "lv_drivers/indev/XPT2046.c" 31 | "lv_drivers/indev/FT5406EE8.c" 32 | "lv_drivers/indev/evdev.c" 33 | "lv_drivers/indev/xkb.c" 34 | "lv_drivers/indev/libinput.c" 35 | "lv_drivers/indev/AD_touch.c" 36 | "lv_drivers/win_drv.c" 37 | "lv_drivers/wayland/wayland.c" 38 | "lv_drivers/gtkdrv/gtkdrv.c" 39 | "lv_drivers/sdl/sdl_gpu.c" 40 | "lv_drivers/sdl/sdl.c" 41 | "lv_drivers/win32drv/win32drv.c" 42 | ) 43 | target_sources( 44 | ${TARGET_NAME} 45 | PRIVATE 46 | ${TARGET_SOURCE_LIST} 47 | ) 48 | 49 | 50 | set_source_files_properties( 51 | ${TARGET_SOURCE_LIST} 52 | PROPERTIES LANGUAGE C 53 | ) 54 | 55 | target_link_libraries( 56 | ${TARGET_NAME} 57 | PRIVATE 58 | lvgl_lib 59 | ) 60 | 61 | if( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR" ) 62 | target_link_libraries(${TARGET_NAME} PUBLIC SDL2::SDL2) 63 | endif() 64 | -------------------------------------------------------------------------------- /Firmware/graphics/platform/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | graphics_backend 3 | gs_platform_layer.cpp 4 | ) 5 | 6 | target_link_libraries( 7 | graphics_backend 8 | PRIVATE 9 | lvgl_lib 10 | UtilsLibrary 11 | logger_service 12 | drivers_ih 13 | etl 14 | ) 15 | 16 | if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) 17 | 18 | target_compile_definitions( 19 | graphics_backend 20 | PRIVATE 21 | USE_HARDWARE_DISPLAY_BACKEND 22 | ) 23 | 24 | target_link_libraries( 25 | graphics_backend 26 | PRIVATE 27 | watch_display 28 | NordicSDK::Common 29 | NordicSDK::App::Timer 30 | ) 31 | elseif( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR" ) 32 | target_compile_definitions( 33 | graphics_backend 34 | PRIVATE 35 | USE_SDL_BACKEND 36 | ) 37 | target_link_libraries( 38 | graphics_backend 39 | PRIVATE 40 | lvgl_drivers 41 | fmt 42 | ) 43 | endif() 44 | 45 | -------------------------------------------------------------------------------- /Firmware/graphics/platform/gs_platform_layer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #ifdef USE_SDL_BACKEND 8 | #include 9 | #include 10 | #include 11 | #endif // USE_WINSDL_BACKEND 12 | 13 | namespace DisplayDriver 14 | { 15 | class IDisplayDriver; 16 | } 17 | 18 | #if defined(USE_HARDWARE_DISPLAY_BACKEND) 19 | #include 20 | #include 21 | #include 22 | 23 | using TSpiBus = Interface::SpiTemplated::SpiBus< 24 | Interface::SpiTemplated::NordicSpi>; 25 | using TDisplayDriver = DisplayDriver::GC9A01Compact; 26 | #endif 27 | 28 | namespace Graphics 29 | { 30 | 31 | class PlatformBackend 32 | { 33 | 34 | public: 35 | PlatformBackend() noexcept; 36 | 37 | void platformDependentInit(lv_disp_drv_t* _displayDriver) noexcept; 38 | 39 | void initPlatformGfxTimer() noexcept; 40 | 41 | void executeLvTaskHandler() noexcept; 42 | 43 | private: 44 | static constexpr std::uint32_t LvglNotificationTime = 15; 45 | 46 | private: 47 | void indevPlatformInit() noexcept; 48 | 49 | void memoryMonitor(lv_timer_t* _param) noexcept; 50 | 51 | private: 52 | #if defined (USE_HARDWARE_DISPLAY_BACKEND) 53 | std::unique_ptr m_hardwareDisplayDriver; 54 | #endif 55 | 56 | #if defined USE_SDL_BACKEND 57 | static inline lv_indev_drv_t m_indevDriver{}; 58 | #endif 59 | }; 60 | 61 | } // namespace Graphics 62 | -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/gs_main_window_event_handler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_ievent_handler.hpp" 4 | 5 | #include "gs_event_handler_base.hpp" 6 | 7 | #include 8 | 9 | namespace Graphics::MainWindow 10 | { 11 | class IGsMainWindowModel; 12 | } 13 | 14 | namespace Graphics::MainWindow 15 | { 16 | 17 | class IBatteryWidget; 18 | 19 | class MainWindowHandler 20 | : public Events::EventHandler 21 | { 22 | 23 | public: 24 | explicit MainWindowHandler(IGsMainWindowModel* _pMainWindowModel) noexcept; 25 | 26 | ~MainWindowHandler() override = default; 27 | 28 | protected: 29 | void handleEventImpl(const Events::TButtonsEvents& _event, const std::any& _eventData) noexcept 30 | override; 31 | 32 | private: 33 | void navigateToNextPage() noexcept; 34 | 35 | void navigateToPreviousPage() noexcept; 36 | 37 | private: 38 | IGsMainWindowModel* m_pMainWindowModel; 39 | }; 40 | 41 | std::unique_ptr createMainWindowEventHandler(IGsMainWindowModel*) noexcept; 42 | 43 | }; // namespace Graphics::MainWindow 44 | -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/gs_main_window_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ih/gs_imain_window.hpp" 3 | #include "ih/gs_itheme_controller.hpp" 4 | 5 | #include 6 | 7 | namespace Graphics::MainWindow 8 | { 9 | 10 | class MainWindowView : public IMainWindowView 11 | { 12 | 13 | public: 14 | MainWindowView() noexcept; 15 | 16 | virtual ~MainWindowView() = default; 17 | 18 | public: 19 | void initBackground() noexcept override; 20 | 21 | void resetBackgroundStyle() noexcept override; 22 | 23 | public: 24 | const Theme::IThemeController* getThemeController() const noexcept override; 25 | 26 | Theme::IThemeController* getThemeController() noexcept override; 27 | 28 | private: 29 | static constexpr inline std::uint32_t Width = LV_HOR_RES_MAX; 30 | static constexpr inline std::uint32_t Height = LV_VER_RES_MAX; 31 | 32 | private: 33 | void initMask() noexcept; 34 | 35 | private: 36 | std::unique_ptr m_pThemeController; 37 | 38 | private: 39 | lv_style_t m_iniStyle; 40 | lv_style_t m_yanStyle; 41 | lv_style_t m_iniCircleStyle; 42 | lv_style_t m_yanCircleStyle; 43 | lv_area_t maskArea; 44 | std::uint16_t m_maskId; 45 | 46 | lv_draw_mask_radius_param_t radiusParam; 47 | Meta::PointerWrapper m_pWindowRoot; 48 | Meta::PointerWrapper m_pIny; 49 | Meta::PointerWrapper m_pInyCircle; 50 | Meta::PointerWrapper m_pYan; 51 | Meta::PointerWrapper m_pYanCircle; 52 | Meta::PointerWrapper m_pObjMask; 53 | }; 54 | 55 | std::unique_ptr createMainWindowView() noexcept; 56 | 57 | } // namespace Graphics::MainWindow 58 | -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/gs_page_view_object.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_ipage_view_object.hpp" 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | namespace Graphics::Events 12 | { 13 | struct TEvent; 14 | } 15 | 16 | namespace Graphics::Views 17 | { 18 | 19 | template class PageViewObject : public ConcretePageView 20 | { 21 | 22 | public: 23 | explicit PageViewObject( 24 | const Theme::IThemeController* _themeController, 25 | std::string_view _pageName) noexcept; 26 | 27 | ~PageViewObject() override = default; 28 | 29 | public: 30 | void addWidget(Graphics::Widgets::IWidgetObject* _pWidget) noexcept override; 31 | 32 | void show() noexcept override; 33 | 34 | void hide() noexcept override; 35 | 36 | void reloadStyle() noexcept override; 37 | 38 | public: 39 | std::string_view getPageName() const noexcept override; 40 | 41 | bool isVisible() const noexcept override; 42 | 43 | const Theme::IThemeController* getThemeController() const noexcept override; 44 | 45 | protected: 46 | virtual void resetStyle() noexcept = 0; 47 | 48 | virtual void initStyles() noexcept = 0; 49 | 50 | virtual void initPageWidgets( 51 | lv_obj_t* _parent, 52 | const std::uint32_t _displayWidth, 53 | const std::uint32_t _displayHeight) noexcept = 0; 54 | 55 | virtual void unloadWidgets() noexcept = 0; 56 | 57 | private: 58 | void executeForEachWidget( 59 | std::function _toCall) noexcept; 60 | 61 | static constexpr int WidgetsQuantity = 3; 62 | using TWidgetsStorage = etl::vector; 63 | TWidgetsStorage m_pWidgetsStorage; 64 | 65 | private: 66 | bool m_isPageVisible; 67 | std::string_view m_pageName; 68 | const Theme::IThemeController* m_pThemeController; 69 | }; 70 | 71 | }; // namespace Graphics::Views 72 | -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/lvgl_ui.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace LvglUi 4 | { 5 | void createWidgetsDemo(); 6 | } -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/lvgl_views_creators/gs_pages_creator.cpp: -------------------------------------------------------------------------------- 1 | #include "gs_pages_creator.hpp" 2 | 3 | #include "widgets_layer/pages/clock_page/gs_clock_page_view.hpp" 4 | #include "widgets_layer/pages/health_page/gs_health_page_view.hpp" 5 | #include "widgets_layer/pages/player_page/gs_player_page_view.hpp" 6 | 7 | namespace Graphics::Views 8 | { 9 | LvglPagesCreator::~LvglPagesCreator() = default; 10 | 11 | std::unique_ptr LvglPagesCreator::createClockPage( 12 | Theme::IThemeController* _pThemeController) noexcept 13 | { 14 | return Views::createClockWatchView(_pThemeController); 15 | } 16 | std::unique_ptr LvglPagesCreator::createHealthPage( 17 | Theme::IThemeController* _pThemeController) noexcept 18 | { 19 | return Views::createHeartrateWatchView(_pThemeController); 20 | } 21 | std::unique_ptr LvglPagesCreator::createPlayerPage( 22 | Theme::IThemeController* _pThemeController) noexcept 23 | { 24 | return Views::createPlayerWatchView(_pThemeController); 25 | } 26 | IPagesCreator::Ptr createLvglPagesCreator() noexcept 27 | { 28 | return std::make_unique(); 29 | } 30 | } // namespace Graphics::Views -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/lvgl_views_creators/gs_pages_creator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/creators/gs_ipages_creator.hpp" 4 | 5 | namespace Graphics::Views 6 | { 7 | 8 | class LvglPagesCreator : public IPagesCreator 9 | { 10 | public: 11 | ~LvglPagesCreator() override; 12 | 13 | public: 14 | std::unique_ptr createClockPage( 15 | Theme::IThemeController* _pThemeController) noexcept override; 16 | 17 | std::unique_ptr createHealthPage( 18 | Theme::IThemeController* _pThemeController) noexcept override; 19 | 20 | std::unique_ptr createPlayerPage( 21 | Theme::IThemeController* _pThemeController) noexcept override; 22 | }; 23 | 24 | IPagesCreator::Ptr createLvglPagesCreator() noexcept; 25 | 26 | } // namespace Graphics::Views -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/lvgl_views_creators/gs_widgets_creator.cpp: -------------------------------------------------------------------------------- 1 | #include "gs_widgets_creator.hpp" 2 | 3 | #include "widgets_layer/widgets/battery/gs_battery_widget.hpp" 4 | 5 | #include "widgets_layer/widgets/pages_switch/gs_pages_switch.hpp" 6 | 7 | #include "widgets_layer/widgets/bluetooth/gs_bluetooth_widget.hpp" 8 | 9 | namespace Graphics::Widgets 10 | { 11 | LvglWidgetsCreator::~LvglWidgetsCreator() = default; 12 | 13 | std::unique_ptr LvglWidgetsCreator::createBluetoothWidget( 14 | Theme::IThemeController* _pThemeController) noexcept 15 | { 16 | return Widgets::createBluetoothWidget(_pThemeController); 17 | } 18 | 19 | std::unique_ptr LvglWidgetsCreator::createPagesSwitchWidget( 20 | Theme::IThemeController* _pThemeController) noexcept 21 | { 22 | return Widgets::createPagesSwitch(_pThemeController); 23 | } 24 | 25 | std::unique_ptr LvglWidgetsCreator::createBatteryWidget( 26 | Theme::IThemeController* _pThemeController) noexcept 27 | { 28 | return Widgets::createBatteryWidget(_pThemeController); 29 | } 30 | 31 | IWidgetsCreator::Ptr createLvglWidgetsCreator() noexcept 32 | { 33 | return std::make_unique(); 34 | } 35 | 36 | } // namespace Graphics::Widgets -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/lvgl_views_creators/gs_widgets_creator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/creators/gs_iwidgets_creator.hpp" 4 | 5 | namespace Graphics::Widgets 6 | { 7 | 8 | class LvglWidgetsCreator : public IWidgetsCreator 9 | { 10 | public: 11 | ~LvglWidgetsCreator() override; 12 | 13 | public: 14 | std::unique_ptr createBluetoothWidget( 15 | Theme::IThemeController* _pThemeController) noexcept override; 16 | 17 | std::unique_ptr createPagesSwitchWidget( 18 | Theme::IThemeController* _pThemeController) noexcept override; 19 | 20 | std::unique_ptr createBatteryWidget( 21 | Theme::IThemeController* _pThemeController) noexcept override; 22 | }; 23 | 24 | IWidgetsCreator::Ptr createLvglWidgetsCreator() noexcept; 25 | 26 | } // namespace Graphics::Widgets -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/pages/clock_page/gs_clock_page_handler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_events.hpp" 4 | #include "ih/gs_ievent_handler.hpp" 5 | 6 | #include "widgets_layer/gs_event_handler_base.hpp" 7 | 8 | #include "utils/TimeWrapper.hpp" 9 | 10 | #include 11 | 12 | namespace Graphics::Views 13 | { 14 | 15 | class IClockWatchPage; 16 | 17 | class ClockPageHandler 18 | : public Events::EventHandler 19 | { 20 | 21 | public: 22 | explicit ClockPageHandler(IClockWatchPage* _clockPageView) noexcept; 23 | 24 | ~ClockPageHandler() override = default; 25 | 26 | protected: 27 | void handleEventImpl(const Events::TDateTimeEvents& _event, const std::any& _eventData) noexcept 28 | override; 29 | 30 | private: 31 | bool shouldApplyNewDate(const TimeWrapper& _toCheck) noexcept; 32 | 33 | static std::string formatToFullDate(const TimeWrapper& _toFormat) noexcept; 34 | 35 | static std::string formatDoubleDigitsNumber(std::uint8_t _toFormat) noexcept; 36 | 37 | private: 38 | bool m_forceUpdateAfterVisibilityChange; 39 | IClockWatchPage* m_pClockWatchView; 40 | 41 | TimeWrapper m_lastReceivedTime; 42 | std::string m_fullDateString; 43 | }; 44 | 45 | Graphics::TEventHandlerPtr createPageWatchHandler(IClockWatchPage* _clockPage) noexcept; 46 | 47 | }; // namespace Graphics::Views 48 | -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/widgets/battery/gs_battery_handler.cpp: -------------------------------------------------------------------------------- 1 | #include "gs_battery_handler.hpp" 2 | 3 | #include "ih/widgets/gs_ibattery_widget.hpp" 4 | 5 | #include "ih/gs_events.hpp" 6 | #include "utils/MetaUtils.hpp" 7 | 8 | namespace 9 | { 10 | Graphics::Widgets::IBatteryWidget::BatteryStatus toBatteryStatus( 11 | std::uint8_t _batteryValue) noexcept 12 | { 13 | if (_batteryValue <= 25) 14 | return Graphics::Widgets::IBatteryWidget::BatteryStatus::QuaterCharge; 15 | else if (_batteryValue > 25 && _batteryValue <= 50) 16 | return Graphics::Widgets::IBatteryWidget::BatteryStatus::HalfCharged; 17 | else if (_batteryValue > 50 && _batteryValue <= 75) 18 | return Graphics::Widgets::IBatteryWidget::BatteryStatus::Charge75Percents; 19 | else if (_batteryValue > 75 && _batteryValue <= 90) 20 | return Graphics::Widgets::IBatteryWidget::BatteryStatus::Charged90Percents; 21 | else 22 | return Graphics::Widgets::IBatteryWidget::BatteryStatus::FullCharged; 23 | } 24 | } // namespace 25 | 26 | namespace Graphics::Widgets 27 | { 28 | 29 | BatteryWidgetHandler::BatteryWidgetHandler(IBatteryWidget* _bateryWidget) noexcept 30 | : m_pBatteryWidget{_bateryWidget} 31 | { 32 | } 33 | 34 | void BatteryWidgetHandler::handleEventImpl( 35 | const Events::TBatteryEvents& _event, 36 | const std::any& _eventData) noexcept 37 | { 38 | if (_event != Events::TBatteryEvents::BatteryLevelChanged) 39 | return; 40 | 41 | std::uint8_t batteryValue = std::any_cast(_eventData); 42 | 43 | if (auto pBatteryWidget = m_pBatteryWidget; pBatteryWidget && pBatteryWidget->isVisible()) 44 | { 45 | pBatteryWidget->setBatteryLevelPercentage(batteryValue); 46 | pBatteryWidget->setBatteryStatus(toBatteryStatus(batteryValue)); 47 | } 48 | } 49 | 50 | std::unique_ptr createBatteryWidgetHandler( 51 | IBatteryWidget* _batteryWidget) noexcept 52 | { 53 | return std::make_unique(_batteryWidget); 54 | } 55 | 56 | } // namespace Graphics::Widgets 57 | -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/widgets/battery/gs_battery_handler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_ievent_handler.hpp" 4 | 5 | #include "widgets_layer/gs_event_handler_base.hpp" 6 | 7 | #include 8 | 9 | namespace Graphics::Widgets 10 | { 11 | 12 | class IBatteryWidget; 13 | 14 | class BatteryWidgetHandler 15 | : public Events::EventHandler 16 | { 17 | 18 | public: 19 | explicit BatteryWidgetHandler(IBatteryWidget* _bateryWidget) noexcept; 20 | 21 | ~BatteryWidgetHandler() override = default; 22 | 23 | protected: 24 | void handleEventImpl(const Events::TBatteryEvents& _event, const std::any& _eventData) noexcept 25 | override; 26 | 27 | private: 28 | IBatteryWidget* m_pBatteryWidget; 29 | }; 30 | 31 | std::unique_ptr createBatteryWidgetHandler(IBatteryWidget*) noexcept; 32 | 33 | }; // namespace Graphics::Widgets 34 | -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/widgets/battery/gs_battery_widget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/widgets/gs_ibattery_widget.hpp" 4 | #include "widgets_layer/widgets/gs_widget_base_obj.hpp" 5 | 6 | #include "lvgl.h" 7 | 8 | #include "utils/MetaUtils.hpp" 9 | 10 | #include 11 | #include 12 | 13 | namespace Graphics::Theme 14 | { 15 | class IThemeController; 16 | } 17 | 18 | namespace Graphics::Widgets 19 | { 20 | 21 | class IWidgetEventHandler; 22 | 23 | class BatteryWidget : public WidgetBaseObj 24 | { 25 | 26 | public: 27 | explicit BatteryWidget(const Theme::IThemeController* _themeController) noexcept; 28 | 29 | ~BatteryWidget() override = default; 30 | 31 | public: 32 | void show() noexcept override; 33 | 34 | void hide() noexcept override; 35 | 36 | void reloadStyle() noexcept override; 37 | 38 | public: 39 | void setBatteryLevelPercentage(const std::uint8_t _newBatteryLevel) noexcept override; 40 | 41 | void setBatteryStatus(BatteryStatus _iconToSet) noexcept override; 42 | 43 | private: 44 | void initStyles() noexcept; 45 | 46 | void resetStyle() noexcept; 47 | 48 | void initBatteryPercentageLabel( 49 | lv_obj_t* _parentObject, 50 | const std::uint32_t _displayWidth, 51 | const std::uint32_t _displayHeight) noexcept; 52 | 53 | void initBatteryIcon( 54 | lv_obj_t* _parentObject, 55 | const std::uint32_t _displayWidth, 56 | const std::uint32_t _displayHeight) noexcept; 57 | 58 | private: 59 | lv_style_t m_bateryIconStyle; 60 | lv_style_t m_batteryLabelStyle; 61 | 62 | std::string m_labelText; 63 | 64 | BatteryStatus m_currentStatus; 65 | 66 | Meta::PointerWrapper m_pBatteryIcon; 67 | Meta::PointerWrapper m_pBatteryLabel; 68 | }; 69 | 70 | std::unique_ptr createBatteryWidget( 71 | const Theme::IThemeController* _themeController) noexcept; 72 | 73 | }; // namespace Graphics::Widgets 74 | -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/widgets/bluetooth/gs_bluetooth_widget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/widgets/gs_ibluetooth_widget.hpp" 4 | #include "widgets_layer/widgets/gs_widget_base_obj.hpp" 5 | 6 | #include "lvgl.h" 7 | 8 | #include "utils/MetaUtils.hpp" 9 | 10 | #include 11 | #include 12 | 13 | namespace Graphics::Theme 14 | { 15 | class IThemeController; 16 | } 17 | 18 | namespace Graphics::Widgets 19 | { 20 | 21 | class IWidgetEventHandler; 22 | 23 | class BluetoothWidget : public WidgetBaseObj 24 | { 25 | 26 | public: 27 | explicit BluetoothWidget(const Theme::IThemeController* _themeController) noexcept; 28 | 29 | ~BluetoothWidget() override = default; 30 | 31 | public: 32 | void show() noexcept override; 33 | 34 | void hide() noexcept override; 35 | 36 | void reloadStyle() noexcept override; 37 | 38 | public: 39 | void setBluetoothStatus(BluetoothStatus _iconToSet) noexcept override; 40 | 41 | private: 42 | void initStyles() noexcept; 43 | 44 | void resetStyle() noexcept; 45 | 46 | void initBluetoothIcon( 47 | lv_obj_t* _parentObject, 48 | const std::uint32_t _displayWidth, 49 | const std::uint32_t _displayHeight) noexcept; 50 | 51 | BluetoothStatus getCurrentStatus() const noexcept; 52 | 53 | private: 54 | BluetoothStatus m_currentStatus; 55 | lv_style_t m_bluetoothIconStyle; 56 | Meta::PointerWrapper m_pBluetoothIcon; 57 | }; 58 | 59 | std::unique_ptr createBluetoothWidget( 60 | const Theme::IThemeController* _themeController) noexcept; 61 | 62 | }; // namespace Graphics::Widgets 63 | -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/widgets/bluetooth/gs_bluetooth_widget_handler.cpp: -------------------------------------------------------------------------------- 1 | #include "gs_bluetooth_widget_handler.hpp" 2 | 3 | #include "ih/gs_events.hpp" 4 | 5 | #include "ih/widgets/gs_ibluetooth_widget.hpp" 6 | 7 | #include 8 | 9 | namespace Graphics::Widgets 10 | { 11 | 12 | BluetoothWidgetHandler::BluetoothWidgetHandler(IBluetoothWidget* _bluetoothWidget) noexcept 13 | : m_pBluetoothWidget{_bluetoothWidget} 14 | { 15 | } 16 | 17 | void BluetoothWidgetHandler::handleEventImpl( 18 | const Events::TBleClientEvents& _event, 19 | const std::any& _eventData) noexcept 20 | { 21 | if (_event == Events::TBleClientEvents::DeviceConnected) 22 | m_pBluetoothWidget->setBluetoothStatus(IBluetoothWidget::BluetoothStatus::Connected); 23 | else if (_event == Events::TBleClientEvents::DeviceDisconnected) 24 | m_pBluetoothWidget->setBluetoothStatus(IBluetoothWidget::BluetoothStatus::Disconnected); 25 | else 26 | { 27 | assert(false); 28 | } 29 | } 30 | 31 | std::unique_ptr createBluetoothWidgetHandler( 32 | IBluetoothWidget* _bluetoothWidget) noexcept 33 | { 34 | return std::make_unique(_bluetoothWidget); 35 | } 36 | 37 | } // namespace Graphics::Widgets 38 | -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/widgets/bluetooth/gs_bluetooth_widget_handler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_ievent_handler.hpp" 4 | 5 | #include "widgets_layer/gs_event_handler_base.hpp" 6 | 7 | #include 8 | 9 | namespace Graphics::Widgets 10 | { 11 | 12 | class IBluetoothWidget; 13 | 14 | class BluetoothWidgetHandler 15 | : public Events::EventHandler 16 | { 17 | 18 | public: 19 | explicit BluetoothWidgetHandler(IBluetoothWidget* _bluetoothWidget) noexcept; 20 | 21 | ~BluetoothWidgetHandler() override = default; 22 | 23 | protected: 24 | void handleEventImpl( 25 | const Events::TBleClientEvents& _event, 26 | const std::any& _eventData) noexcept override; 27 | 28 | private: 29 | IBluetoothWidget* m_pBluetoothWidget; 30 | }; 31 | 32 | std::unique_ptr createBluetoothWidgetHandler( 33 | IBluetoothWidget* _bluetoothWidget) noexcept; 34 | 35 | } // namespace Graphics::Widgets -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/widgets/gs_widget_base_obj.cpp: -------------------------------------------------------------------------------- 1 | #include "gs_widget_base_obj.hpp" 2 | 3 | #include "ih/gs_events.hpp" 4 | #include "ih/gs_itheme_controller.hpp" 5 | 6 | #include "ih/widgets/gs_ibattery_widget.hpp" 7 | #include "ih/widgets/gs_ibluetooth_widget.hpp" 8 | #include "ih/widgets/gs_ipages_switch.hpp" 9 | 10 | namespace Graphics::Widgets 11 | { 12 | 13 | template 14 | WidgetBaseObj::WidgetBaseObj( 15 | const Theme::IThemeController* _themeController) noexcept 16 | : m_isWidgetVisible{false}, m_pThemeController{_themeController} 17 | { 18 | } 19 | 20 | template void WidgetBaseObj::show() noexcept 21 | { 22 | m_isWidgetVisible = true; 23 | } 24 | 25 | template void WidgetBaseObj::hide() noexcept 26 | { 27 | m_isWidgetVisible = false; 28 | } 29 | 30 | template 31 | bool WidgetBaseObj::isVisible() const noexcept 32 | { 33 | return m_isWidgetVisible; 34 | } 35 | 36 | template 37 | typename WidgetBaseObj::ShowParams WidgetBaseObj< 38 | TBaseWidgetInterface>::getShowParams() noexcept 39 | { 40 | auto pThemeProvider = getThemeController(); 41 | if (!pThemeProvider) 42 | return {}; 43 | 44 | auto parent = lv_disp_get_scr_act(nullptr); 45 | 46 | return WidgetBaseObj::ShowParams{ 47 | parent, pThemeProvider->getDisplayWidth(), pThemeProvider->getDisplayHeight()}; 48 | } 49 | 50 | template 51 | const Theme::IThemeController* WidgetBaseObj::getThemeController() 52 | const noexcept 53 | { 54 | return m_pThemeController; 55 | }; 56 | 57 | template class WidgetBaseObj; 58 | template class WidgetBaseObj; 59 | template class WidgetBaseObj; 60 | 61 | }; // namespace Graphics::Widgets 62 | -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/widgets/gs_widget_base_obj.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/gs_iwidget_object.hpp" 4 | 5 | #include "lvgl.h" 6 | 7 | #include "utils/MetaUtils.hpp" 8 | 9 | #include 10 | #include 11 | 12 | namespace Graphics::Widgets 13 | { 14 | 15 | template class WidgetBaseObj : public TBaseWidgetInterface 16 | { 17 | 18 | public: 19 | explicit WidgetBaseObj(const Theme::IThemeController* _themeController) noexcept; 20 | 21 | public: 22 | void show() noexcept override; 23 | 24 | void hide() noexcept override; 25 | 26 | bool isVisible() const noexcept override; 27 | 28 | public: 29 | const Theme::IThemeController* getThemeController() const noexcept override; 30 | 31 | protected: 32 | struct ShowParams 33 | { 34 | lv_obj_t* pParent; 35 | const std::uint32_t DisplayWidth; 36 | const std::uint32_t DisplayHeight; 37 | }; 38 | 39 | ShowParams getShowParams() noexcept; 40 | 41 | private: 42 | bool m_isWidgetVisible; 43 | 44 | const Theme::IThemeController* m_pThemeController; 45 | }; 46 | 47 | } // namespace Graphics::Widgets -------------------------------------------------------------------------------- /Firmware/graphics/widgets_layer/widgets/pages_switch/gs_pages_switch.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/widgets/gs_ipages_switch.hpp" 4 | #include "widgets_layer/widgets/gs_widget_base_obj.hpp" 5 | 6 | #include "lvgl.h" 7 | 8 | #include "utils/MetaUtils.hpp" 9 | 10 | #include 11 | #include 12 | 13 | namespace Graphics::Theme 14 | { 15 | class IThemeController; 16 | } 17 | 18 | namespace Graphics::Widgets 19 | { 20 | 21 | class PagesSwitch : public WidgetBaseObj 22 | { 23 | 24 | public: 25 | explicit PagesSwitch(const Theme::IThemeController* _themeController) noexcept; 26 | 27 | ~PagesSwitch() override = default; 28 | 29 | public: 30 | void show() noexcept override; 31 | 32 | void hide() noexcept override; 33 | 34 | void reloadStyle() noexcept override; 35 | 36 | public: 37 | void setActivePage(std::string_view _pageName) noexcept override; 38 | 39 | private: 40 | void initStyles() noexcept; 41 | 42 | void resetStyle() noexcept; 43 | 44 | void initCheckedPages( 45 | lv_obj_t* _parentObject, 46 | const std::uint32_t _displayWidth, 47 | const std::uint32_t _displayHeight) noexcept; 48 | 49 | void initUncheckedPages( 50 | lv_obj_t* _parentObject, 51 | const std::uint32_t _displayWidth, 52 | const std::uint32_t _displayHeight) noexcept; 53 | 54 | private: 55 | static constexpr inline std::uint8_t ArcSize = 16; 56 | 57 | private: 58 | lv_style_t m_pointStyle; 59 | lv_style_t m_pointStyleChecked; 60 | 61 | std::string_view m_activePageName; 62 | 63 | Meta::PointerWrapper m_pFirstPage; 64 | Meta::PointerWrapper m_pSecondPage; 65 | Meta::PointerWrapper m_pThirdPage; 66 | }; 67 | 68 | std::unique_ptr createPagesSwitch( 69 | const Theme::IThemeController* _themeController) noexcept; 70 | 71 | }; // namespace Graphics::Widgets 72 | -------------------------------------------------------------------------------- /Firmware/logger/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | logger_service 3 | logger_service_impl.cpp 4 | ) 5 | 6 | target_include_directories( 7 | logger_service 8 | PUBLIC 9 | ${CMAKE_CURRENT_LIST_DIR}/inc 10 | UtilsLibrary 11 | ) 12 | 13 | target_compile_features( 14 | logger_service 15 | PUBLIC 16 | cxx_std_20 17 | ) 18 | 19 | target_link_libraries( 20 | logger_service 21 | PUBLIC 22 | UtilsLibrary 23 | PRIVATE 24 | ) 25 | 26 | if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) 27 | 28 | target_compile_definitions( 29 | logger_service 30 | PRIVATE 31 | LoggerSegger 32 | ) 33 | 34 | target_link_libraries(logger_service 35 | PRIVATE 36 | NordicSDK::Common 37 | ) 38 | elseif( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR" ) 39 | target_compile_definitions( 40 | logger_service 41 | PRIVATE 42 | LoggerDesktop 43 | ) 44 | target_link_libraries( 45 | logger_service 46 | PRIVATE 47 | fmt::fmt 48 | ) 49 | endif() -------------------------------------------------------------------------------- /Firmware/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ap_application.hpp" 2 | 3 | int main(void) 4 | { 5 | 6 | Application mainApp; 7 | 8 | mainApp.runApplicationLoop(); 9 | 10 | return 0; 11 | } -------------------------------------------------------------------------------- /Firmware/old_toolchain_files/CMakeListsOldRoot.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | option( EXECUTE_MCU_FLASHING "Execute binary flashingto target MCU" ON ) 4 | option( USE_ENV_SDK_PATH "Use Nordic SDK path from Environment variables" ON ) 5 | option( USE_ENV_GCC_PATH "Use GCC toolchain path from Environment variables" ON ) 6 | 7 | set(NRF_TARGET nrf52) 8 | 9 | if (NOT DEFINED ARM_NONE_EABI_TOOLCHAIN_PATH AND USE_ENV_GCC_PATH ) 10 | set(ARM_NONE_EABI_TOOLCHAIN_PATH "C:/gcc_none_eabi_9_2_1") 11 | else() 12 | set( ARM_NONE_EABI_TOOLCHAIN_PATH ${GCC_TOOLCHAIN_EXT_PATH} ) 13 | endif() 14 | 15 | set(NRF5_SDK_PATH "") 16 | 17 | if ( USE_ENV_SDK_PATH ) 18 | set(NRF5_SDK_PATH $ENV{NRF5_SDK_PATH_ENV}) 19 | else() 20 | set(NRF5_SDK_PATH ${NRF5_SDK_PATH_EXT}) 21 | endif() 22 | 23 | 24 | if( EXECUTE_MCU_FLASHING ) 25 | set(NRFJPROG $ENV{NRFJPROG_PATH}/nrfjprog.exe ) 26 | endif(EXECUTE_MCU_FLASHING) 27 | 28 | include("${CMAKE_CURRENT_LIST_DIR}/../CMake_nRF5x.cmake") 29 | 30 | nRF5x_toolchainSetup() 31 | 32 | project(ExampleProject C) 33 | 34 | add_subdirectory(src) -------------------------------------------------------------------------------- /Firmware/old_toolchain_files/arm-gcc-toolchain.cmake: -------------------------------------------------------------------------------- 1 | # check if already run. Changing the compiler var can cause reconfigure so don't want to do it again 2 | if(DEFINED ARM_GCC_TOOLCHAIN) 3 | return() 4 | endif() 5 | set(ARM_GCC_TOOLCHAIN TRUE) 6 | 7 | set(CMAKE_SYSTEM_NAME Generic) 8 | set(CMAKE_SYSTEM_PROCESSOR ARM) 9 | 10 | set(TOOLCHAIN_PREFIX arm-none-eabi-) 11 | 12 | if (NOT DEFINED ARM_NONE_EABI_TOOLCHAIN_BIN_PATH) 13 | if(MINGW OR CYGWIN OR WIN32) 14 | set(UTIL_SEARCH_CMD where) 15 | elseif(UNIX OR APPLE) 16 | set(UTIL_SEARCH_CMD which) 17 | endif() 18 | execute_process( 19 | COMMAND ${UTIL_SEARCH_CMD} ${TOOLCHAIN_PREFIX}gcc 20 | OUTPUT_VARIABLE BINUTILS_PATH 21 | OUTPUT_STRIP_TRAILING_WHITESPACE 22 | ) 23 | 24 | get_filename_component(ARM_NONE_EABI_TOOLCHAIN_BIN_PATH ${BINUTILS_PATH} DIRECTORY) 25 | endif () 26 | 27 | # Without that flag CMake is not able to pass test compilation check 28 | if (${CMAKE_VERSION} VERSION_EQUAL "3.6.0" OR ${CMAKE_VERSION} VERSION_GREATER "3.6") 29 | set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) 30 | else() 31 | set(CMAKE_EXE_LINKER_FLAGS_INIT "--specs=nosys.specs") 32 | endif() 33 | 34 | set(TOOLCHAIN_PATH_AND_PREFIX ${ARM_NONE_EABI_TOOLCHAIN_BIN_PATH}/${TOOLCHAIN_PREFIX}) 35 | 36 | 37 | if(MINGW OR CYGWIN OR WIN32) 38 | set(CMAKE_C_COMPILER ${TOOLCHAIN_PATH_AND_PREFIX}gcc.exe) 39 | set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER}) 40 | set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PATH_AND_PREFIX}c++.exe) 41 | else() 42 | set(CMAKE_C_COMPILER ${TOOLCHAIN_PATH_AND_PREFIX}gcc) 43 | set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER}) 44 | set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PATH_AND_PREFIX}c++) 45 | endif() 46 | 47 | set(CMAKE_AR ${TOOLCHAIN_PATH_AND_PREFIX}ar) 48 | set(CMAKE_RANLIB ${TOOLCHAIN_PATH_AND_PREFIX}ranlib) 49 | 50 | set(CMAKE_OBJCOPY ${TOOLCHAIN_PATH_AND_PREFIX}objcopy CACHE INTERNAL "objcopy tool") 51 | set(CMAKE_SIZE_UTIL ${TOOLCHAIN_PATH_AND_PREFIX}size CACHE INTERNAL "size tool") 52 | 53 | set(CMAKE_SYSROOT ${ARM_NONE_EABI_TOOLCHAIN_BIN_PATH}) 54 | set(CMAKE_FIND_ROOT_PATH ${ARM_NONE_EABI_TOOLCHAIN_BIN_PATH}) 55 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 56 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 57 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -------------------------------------------------------------------------------- /Firmware/service_providers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | set ( SERVICES_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR} ) 4 | add_subdirectory(headers) 5 | add_subdirectory(watch_fake_services) 6 | -------------------------------------------------------------------------------- /Firmware/service_providers/headers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( services_ih INTERFACE ) 2 | target_include_directories( services_ih INTERFACE ${CMAKE_CURRENT_LIST_DIR} ) -------------------------------------------------------------------------------- /Firmware/service_providers/headers/ih/sp_ibattery_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils/SimpleSignal.hpp" 4 | 5 | #include 6 | 7 | namespace ServiceProviders::BatteryService::Settings 8 | { 9 | using namespace std::chrono_literals; 10 | constexpr std::chrono::seconds MeasurmentPeriod = 1s; 11 | 12 | constexpr std::uint8_t MinBatteryLevel = 0; 13 | constexpr std::uint8_t MaxBatteryLevel = 100; 14 | } // namespace ServiceProviders::BatteryService::Settings 15 | 16 | namespace ServiceProviders::BatteryService 17 | { 18 | 19 | class IBatteryLevelAppService 20 | { 21 | 22 | public: 23 | virtual ~IBatteryLevelAppService() = default; 24 | 25 | public: 26 | virtual std::chrono::seconds getMeasurmentPeriod() const noexcept = 0; 27 | 28 | virtual void startBatteryMeasure() noexcept = 0; 29 | 30 | Simple::Signal onBatteryLevelChangedSig; 31 | }; 32 | 33 | } // namespace ServiceProviders::BatteryService 34 | -------------------------------------------------------------------------------- /Firmware/service_providers/headers/ih/sp_idatetime_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils/SimpleSignal.hpp" 4 | #include "utils/TimeWrapper.hpp" 5 | 6 | namespace ServiceProviders::DateTimeService::Settings 7 | { 8 | } 9 | 10 | namespace ServiceProviders::DateTimeService 11 | { 12 | 13 | class IDateTimeService 14 | { 15 | 16 | public: 17 | virtual ~IDateTimeService() = default; 18 | 19 | virtual void launchService() noexcept = 0; 20 | 21 | virtual void calibrateSource() noexcept = 0; 22 | 23 | virtual void syncronizeWithBleDts() noexcept = 0; 24 | 25 | public: 26 | Simple::Signal onDateTimeChanged; 27 | }; 28 | 29 | } // namespace ServiceProviders::DateTimeService 30 | -------------------------------------------------------------------------------- /Firmware/service_providers/headers/ih/sp_iheartrate_service.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils/SimpleSignal.hpp" 4 | 5 | #include 6 | 7 | namespace ServiceProviders::HeartrateService::Settings 8 | { 9 | using namespace std::chrono_literals; 10 | constexpr std::chrono::seconds MeasurmentPeriod = 12s; 11 | } // namespace ServiceProviders::HeartrateService::Settings 12 | 13 | namespace ServiceProviders::HeartrateService 14 | { 15 | 16 | class IHeartrateService 17 | { 18 | 19 | public: 20 | virtual ~IHeartrateService() = default; 21 | 22 | virtual void startMeasure() noexcept = 0; 23 | 24 | Simple::Signal onMeasureStarted; 25 | Simple::Signal onMeasureFailed; 26 | 27 | Simple::Signal onHeartrateDataReady; 28 | }; 29 | 30 | } // namespace ServiceProviders::HeartrateService 31 | -------------------------------------------------------------------------------- /Firmware/service_providers/headers/ih/sp_iservice_creator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace ServiceProviders::BatteryService 6 | { 7 | class IBatteryLevelAppService; 8 | } 9 | 10 | namespace ServiceProviders::HeartrateService 11 | { 12 | class IHeartrateService; 13 | } 14 | 15 | namespace ServiceProviders::DateTimeService 16 | { 17 | class IDateTimeService; 18 | } 19 | 20 | namespace ServiceProviders 21 | { 22 | 23 | class IServiceCreator 24 | { 25 | 26 | public: 27 | [[nodiscard]] virtual std::unique_ptr 28 | getBatteryService() noexcept = 0; 29 | 30 | [[nodiscard]] virtual std::unique_ptr 31 | getHeartrateService() noexcept = 0; 32 | 33 | [[nodiscard]] virtual std::unique_ptr 34 | getDateTimeService() noexcept = 0; 35 | 36 | virtual ~IServiceCreator() = default; 37 | }; 38 | 39 | } // namespace ServiceProviders -------------------------------------------------------------------------------- /Firmware/service_providers/watch_fake_services/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | add_subdirectory(battery) 4 | add_subdirectory(datetime) 5 | add_subdirectory(heartrate) 6 | 7 | add_library( 8 | watch_fake_services 9 | sp_fake_services_creator.cpp 10 | ) 11 | 12 | target_include_directories( 13 | watch_fake_services 14 | PRIVATE 15 | services_ih 16 | PUBLIC 17 | ${CMAKE_CURRENT_LIST_DIR}/inc 18 | ) 19 | 20 | target_link_libraries( 21 | watch_fake_services 22 | PRIVATE 23 | services_ih 24 | UtilsLibrary 25 | etl 26 | battery_fake_service 27 | datetime_fake_service 28 | heartrate_fake_service 29 | ) 30 | 31 | target_link_options( 32 | watch_fake_services 33 | PUBLIC 34 | ${CPU_FLAGS} 35 | ) 36 | 37 | target_compile_options( 38 | watch_fake_services 39 | PUBLIC 40 | $<$:-Os> 41 | $<$:-Os> 42 | ${COMMON_FLAGS} 43 | ) 44 | target_compile_features( watch_fake_services PUBLIC cxx_std_20 ) -------------------------------------------------------------------------------- /Firmware/service_providers/watch_fake_services/battery/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | battery_fake_service 3 | sp_battery_service_fake.cpp 4 | ) 5 | 6 | target_include_directories( 7 | battery_fake_service 8 | PRIVATE 9 | services_ih 10 | ${CMAKE_CURRENT_LIST_DIR}/inc 11 | ) 12 | 13 | target_link_libraries( 14 | battery_fake_service 15 | PRIVATE 16 | services_ih 17 | UtilsLibrary 18 | ) 19 | 20 | if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) 21 | target_link_libraries(battery_fake_service 22 | PRIVATE 23 | NordicSDK::App::SensorSimulator 24 | ) 25 | 26 | target_compile_definitions( 27 | battery_fake_service 28 | PRIVATE 29 | USE_NRFSDK_SIMULATOR 30 | ) 31 | elseif( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR") 32 | target_compile_definitions( 33 | battery_fake_service 34 | PRIVATE 35 | USE_DESKTOP_SIMULATOR 36 | ) 37 | endif() -------------------------------------------------------------------------------- /Firmware/service_providers/watch_fake_services/battery/sp_battery_service_fake.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/sp_ibattery_service.hpp" 4 | 5 | namespace ServiceProviders::BatteryService 6 | { 7 | 8 | class BatteryServiceFake : public IBatteryLevelAppService 9 | { 10 | 11 | public: 12 | explicit BatteryServiceFake(std::chrono::seconds _measurementPeriod) noexcept; 13 | 14 | ~BatteryServiceFake() override; 15 | 16 | public: 17 | std::chrono::seconds getMeasurmentPeriod() const noexcept override; 18 | 19 | void startBatteryMeasure() noexcept override; 20 | 21 | private: 22 | class BatterySimulatorImpl; 23 | std::unique_ptr m_pBatterySimImpl; 24 | }; 25 | 26 | } // namespace ServiceProviders::BatteryService -------------------------------------------------------------------------------- /Firmware/service_providers/watch_fake_services/datetime/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | datetime_fake_service 3 | sp_datetime_service_fake.cpp 4 | ) 5 | 6 | target_include_directories( 7 | datetime_fake_service 8 | PRIVATE 9 | services_ih 10 | ${CMAKE_CURRENT_LIST_DIR}/inc 11 | ) 12 | 13 | target_link_libraries( 14 | datetime_fake_service 15 | PRIVATE 16 | services_ih 17 | UtilsLibrary 18 | ) 19 | 20 | target_compile_features( 21 | datetime_fake_service 22 | PRIVATE 23 | cxx_std_20 24 | ) 25 | 26 | if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) 27 | target_link_libraries(datetime_fake_service 28 | PRIVATE 29 | NordicSDK::App::SensorSimulator 30 | NordicSDK::App::Timer 31 | ) 32 | 33 | target_compile_definitions( 34 | datetime_fake_service 35 | PRIVATE 36 | USE_NRFSDK_SIMULATOR 37 | ) 38 | elseif( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR") 39 | target_compile_definitions( 40 | datetime_fake_service 41 | PRIVATE 42 | USE_DESKTOP_SIMULATOR 43 | ) 44 | endif() -------------------------------------------------------------------------------- /Firmware/service_providers/watch_fake_services/datetime/sp_datetime_service_fake.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/sp_idatetime_service.hpp" 4 | 5 | namespace ServiceProviders::DateTimeService 6 | { 7 | 8 | class DateTimeServiceFake : public IDateTimeService 9 | { 10 | 11 | public: 12 | explicit DateTimeServiceFake() noexcept; 13 | 14 | ~DateTimeServiceFake() override; 15 | 16 | public: 17 | void launchService() noexcept override; 18 | 19 | void calibrateSource() noexcept override; 20 | 21 | void syncronizeWithBleDts() noexcept override; 22 | 23 | private: 24 | class DatetimeSimulatorImpl; 25 | std::unique_ptr m_pDatetimeSimImpl; 26 | }; 27 | 28 | } // namespace ServiceProviders::DateTimeService -------------------------------------------------------------------------------- /Firmware/service_providers/watch_fake_services/heartrate/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set (TARGET_NAME heartrate_fake_service) 2 | 3 | add_library( 4 | ${TARGET_NAME} 5 | sp_heartrate_service_fake.cpp 6 | ) 7 | 8 | target_compile_definitions( 9 | ${TARGET_NAME} 10 | PRIVATE 11 | USE_NRFSDK_SIMULATOR 12 | ) 13 | 14 | target_include_directories( 15 | ${TARGET_NAME} 16 | PRIVATE 17 | services_ih 18 | ${CMAKE_CURRENT_LIST_DIR}/inc 19 | ) 20 | 21 | target_link_libraries( 22 | ${TARGET_NAME} 23 | PRIVATE 24 | services_ih 25 | UtilsLibrary 26 | ) 27 | 28 | target_link_options( 29 | ${TARGET_NAME} 30 | PUBLIC 31 | ${CPU_FLAGS} 32 | ) 33 | 34 | target_compile_options( 35 | ${TARGET_NAME} 36 | PUBLIC 37 | $<$:-Os> 38 | $<$:-Os> 39 | ${COMMON_FLAGS} 40 | ) 41 | -------------------------------------------------------------------------------- /Firmware/service_providers/watch_fake_services/heartrate/sp_heartrate_service_fake.cpp: -------------------------------------------------------------------------------- 1 | #include "sp_heartrate_service_fake.hpp" 2 | 3 | namespace ServiceProviders::HeartrateService 4 | { 5 | 6 | class HeartrateServiceFake::HeartrateSimulatorImpl 7 | { 8 | 9 | public: 10 | void startMeasure() noexcept {}; // Just for test 11 | }; 12 | 13 | HeartrateServiceFake::HeartrateServiceFake() noexcept 14 | : m_pHeartrateSimImpl{std::make_unique()} 15 | { 16 | } 17 | 18 | HeartrateServiceFake::~HeartrateServiceFake() = default; 19 | 20 | void HeartrateServiceFake::startMeasure() noexcept 21 | { 22 | m_pHeartrateSimImpl->startMeasure(); 23 | } 24 | 25 | } // namespace ServiceProviders::HeartrateService -------------------------------------------------------------------------------- /Firmware/service_providers/watch_fake_services/heartrate/sp_heartrate_service_fake.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/sp_iheartrate_service.hpp" 4 | 5 | #include 6 | 7 | namespace ServiceProviders::HeartrateService 8 | { 9 | 10 | class HeartrateServiceFake : public IHeartrateService 11 | { 12 | 13 | public: 14 | explicit HeartrateServiceFake() noexcept; 15 | 16 | ~HeartrateServiceFake(); 17 | 18 | public: 19 | void startMeasure() noexcept override; 20 | 21 | private: 22 | class HeartrateSimulatorImpl; 23 | std::unique_ptr m_pHeartrateSimImpl; 24 | }; 25 | 26 | } // namespace ServiceProviders::HeartrateService 27 | -------------------------------------------------------------------------------- /Firmware/service_providers/watch_fake_services/inc/sp_fake_services_creator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ih/sp_iservice_creator.hpp" 4 | 5 | namespace ServiceProviders 6 | { 7 | 8 | class FakeServiceCreator : public IServiceCreator 9 | { 10 | 11 | public: 12 | [[nodiscard]] std::unique_ptr 13 | getBatteryService() noexcept override; 14 | 15 | [[nodiscard]] std::unique_ptr 16 | getHeartrateService() noexcept override; 17 | 18 | [[nodiscard]] std::unique_ptr getDateTimeService() noexcept 19 | override; 20 | 21 | ~FakeServiceCreator() override = default; 22 | }; 23 | 24 | std::unique_ptr getFakeServiceCreator() noexcept; 25 | 26 | } // namespace ServiceProviders -------------------------------------------------------------------------------- /Firmware/service_providers/watch_fake_services/sp_fake_services_creator.cpp: -------------------------------------------------------------------------------- 1 | #include "inc/sp_fake_services_creator.hpp" 2 | 3 | #include "ih/sp_ibattery_service.hpp" 4 | #include "ih/sp_iservice_creator.hpp" 5 | 6 | #include "battery/sp_battery_service_fake.hpp" 7 | #include "datetime/sp_datetime_service_fake.hpp" 8 | #include "heartrate/sp_heartrate_service_fake.hpp" 9 | 10 | namespace ServiceProviders 11 | { 12 | 13 | std::unique_ptr FakeServiceCreator:: 14 | getBatteryService() noexcept 15 | { 16 | return std::make_unique( 17 | BatteryService::Settings::MeasurmentPeriod); 18 | } 19 | 20 | std::unique_ptr FakeServiceCreator:: 21 | getHeartrateService() noexcept 22 | { 23 | return std::make_unique(); 24 | } 25 | 26 | std::unique_ptr FakeServiceCreator::getDateTimeService() noexcept 27 | { 28 | return std::make_unique(); 29 | } 30 | 31 | std::unique_ptr getFakeServiceCreator() noexcept 32 | { 33 | return std::make_unique(); 34 | } 35 | 36 | }; // namespace ServiceProviders 37 | -------------------------------------------------------------------------------- /Firmware/utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( UtilsLibrary INTERFACE ) 2 | 3 | target_compile_features( 4 | UtilsLibrary 5 | INTERFACE 6 | cxx_std_20 7 | ) 8 | 9 | target_include_directories( 10 | UtilsLibrary 11 | INTERFACE 12 | ${CMAKE_CURRENT_LIST_DIR} 13 | ${CMAKE_CURRENT_LIST_DIR}/inc 14 | ${CMAKE_CURRENT_LIST_DIR}/inc/utils/coroutine 15 | ) 16 | 17 | if( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR" ) 18 | target_compile_definitions( 19 | UtilsLibrary 20 | INTERFACE 21 | USE_DESKTOP_SIMULATOR 22 | _CRT_SECURE_NO_WARNINGS 23 | ) 24 | target_compile_options(UtilsLibrary INTERFACE $<$:-fcoroutines>) 25 | elseif( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) 26 | target_compile_options( 27 | UtilsLibrary 28 | INTERFACE 29 | -fcoroutines 30 | ) 31 | endif() 32 | 33 | target_link_libraries( 34 | UtilsLibrary 35 | INTERFACE 36 | etl 37 | logger_service 38 | ) 39 | target_compile_options( 40 | UtilsLibrary 41 | INTERFACE 42 | $<$:-fcoroutines> 43 | ) 44 | -------------------------------------------------------------------------------- /Firmware/utils/etl_profile.h: -------------------------------------------------------------------------------- 1 | ///\file 2 | 3 | /****************************************************************************** 4 | The MIT License(MIT) 5 | Embedded Template Library. 6 | https://github.com/ETLCPP/etl 7 | https://www.etlcpp.com 8 | Copyright(c) 2018 jwellbelove 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files(the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions : 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | ******************************************************************************/ 25 | 26 | #ifndef ETL_CPP17_INCLUDED 27 | #define ETL_CPP17_INCLUDED 28 | 29 | //***************************************************************************** 30 | // Generic C++17 31 | //***************************************************************************** 32 | 33 | #define ETL_TARGET_DEVICE_GENERIC 34 | #define ETL_TARGET_OS_NONE 35 | #define ETL_COMPILER_GENERIC 36 | #define ETL_CPP11_SUPPORTED 1 37 | #define ETL_CPP14_SUPPORTED 1 38 | #define ETL_CPP17_SUPPORTED 1 39 | #define ETL_NO_NULLPTR_SUPPORT 0 40 | #define ETL_NO_LARGE_CHAR_SUPPORT 0 41 | #define ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED 1 42 | 43 | #endif -------------------------------------------------------------------------------- /Firmware/utils/inc/utils/CoroUtils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "coroutine/Common.hpp" 4 | #include "coroutine/Event.hpp" 5 | #include "coroutine/ExecutionQueueCoro.hpp" 6 | #include "coroutine/Task.hpp" 7 | #include "coroutine/WhenAllSequence.hpp" 8 | #include "coroutine/WnenAllReady.hpp" -------------------------------------------------------------------------------- /Firmware/utils/inc/utils/FastPimpl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // https://www.youtube.com/watch?v=_AkF8SpUV3k&t=994s 8 | 9 | namespace Utils 10 | { 11 | 12 | template class FastPimpl 13 | { 14 | 15 | public: 16 | template explicit FastPimpl(Args&&... _args) 17 | { 18 | new (Ptr()) TImplementation(std::forward(_args)...); 19 | } 20 | 21 | FastPimpl& operator=(FastPimpl&& _rhs) 22 | { 23 | *Ptr() = std::move(*_rhs); 24 | return *this; 25 | } 26 | 27 | ~FastPimpl() noexcept 28 | { 29 | validate(); 30 | Ptr()->~TImplementation(); 31 | } 32 | 33 | public: 34 | TImplementation* operator->() noexcept 35 | { 36 | return Ptr(); 37 | } 38 | 39 | TImplementation* get() noexcept 40 | { 41 | return Ptr(); 42 | } 43 | 44 | const TImplementation* get() const noexcept 45 | { 46 | return Ptr(); 47 | } 48 | 49 | const TImplementation* operator->() const noexcept 50 | { 51 | return Ptr(); 52 | } 53 | 54 | TImplementation& operator*() noexcept 55 | { 56 | return *Ptr(); 57 | } 58 | 59 | const TImplementation& operator*() const noexcept 60 | { 61 | return *Ptr(); 62 | } 63 | 64 | private: 65 | TImplementation* Ptr() noexcept 66 | { 67 | return reinterpret_cast(&m_pAlignedStorage); 68 | } 69 | 70 | const TImplementation* Ptr() const noexcept 71 | { 72 | return reinterpret_cast(&m_pAlignedStorage); 73 | } 74 | 75 | template static void validate() noexcept 76 | { 77 | static_assert(ActualSize == TypeSizeof, "Size and sizeof(TImplementation) mismatch"); 78 | static_assert(Aligment == ActualAligment, "Aligment and alignof(ActualAligment) mismatch"); 79 | } 80 | 81 | private: 82 | std::aligned_storage m_pAlignedStorage; 83 | }; 84 | 85 | } // namespace Utils -------------------------------------------------------------------------------- /Firmware/utils/inc/utils/MetaUtils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Meta 10 | { 11 | 12 | template 13 | constexpr auto revreseImpl(const TArray& a, std::size_t _arraySize, std::index_sequence) 14 | { 15 | return std::array{a[_arraySize - Index - 1]...}; 16 | } 17 | 18 | template > 19 | constexpr auto reverseArray(const std::array& _array) 20 | { 21 | return revreseImpl(_array, N, Indexes{}); 22 | } 23 | 24 | template 25 | constexpr auto tupleApplyImpl(Callable&& _toCall, const Tuple& _tuple, std::index_sequence) 26 | { 27 | return (_toCall(std::get(_tuple)), ...); 28 | } 29 | template 30 | constexpr auto tupleApply(Callable&& _toCall, const std::tuple& _tuple) 31 | { 32 | return tupleApplyImpl( 33 | std::forward(_toCall), _tuple, std::index_sequence_for{}); 34 | } 35 | 36 | template 37 | using CustomDeleter = std::integral_constant; 38 | 39 | template 40 | using PointerWrapper = std::unique_ptr>; 41 | 42 | template constexpr void UnuseVar(Unused&& _toUnuse) 43 | { 44 | static_cast(_toUnuse); 45 | } 46 | 47 | template struct TypeList; 48 | 49 | template struct TypeList 50 | { 51 | using Head = H; 52 | using Tail = TypeList; 53 | }; 54 | 55 | template <> struct TypeList<> 56 | { 57 | }; 58 | 59 | template struct HasType 60 | { 61 | static int const value = std::is_same_v || 62 | HasType::value; 63 | }; 64 | 65 | template struct HasType> 66 | { 67 | static const bool value = false; 68 | }; 69 | 70 | template struct overload : Ts... 71 | { 72 | using Ts::operator()...; 73 | }; 74 | template overload(Ts...) -> overload; 75 | 76 | }; // namespace Meta 77 | -------------------------------------------------------------------------------- /Firmware/utils/inc/utils/Noncopyable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Utils 4 | { 5 | 6 | struct noncopyable 7 | { 8 | noncopyable() = default; 9 | noncopyable( const noncopyable& ) = delete; 10 | noncopyable& operator= ( const noncopyable&) = delete; 11 | ~noncopyable() = default; 12 | }; 13 | 14 | } -------------------------------------------------------------------------------- /Firmware/utils/inc/utils/Platform.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace Platform 6 | { 7 | 8 | #if defined(USE_DESKTOP_SIMULATOR) 9 | static constexpr size_t LogerImplSize = 1; 10 | static constexpr size_t LogerImplAlignment = 1; 11 | 12 | static constexpr size_t GraphicsBackendSize = 16; 13 | static constexpr size_t GraphicsBackendAlignment = 8; 14 | 15 | static constexpr size_t DateTimeImplSize = 224; 16 | static constexpr size_t DateTimeAlignment = 8; 17 | 18 | static constexpr size_t BatteryImplSize = 192; 19 | static constexpr size_t BatteryAlignment = 8; 20 | #else 21 | static constexpr size_t LogerImplSize = 1; 22 | static constexpr size_t LogerImplAlignment = 1; 23 | 24 | static constexpr size_t SpiImplSize = 12; 25 | static constexpr size_t SpiImplAlignment = 4; 26 | 27 | static constexpr size_t GpioImplSize = 8; 28 | static constexpr size_t GpioImplAlignment = 4; 29 | 30 | static constexpr size_t GraphicsBackendSize = 4; 31 | static constexpr size_t GraphicsBackendAlignment = 4; 32 | 33 | static constexpr size_t GlLvglServiceImplSize = 60; 34 | static constexpr size_t GlLvglServiceAlignment = 4; 35 | 36 | static constexpr size_t DateTimeImplSize = 40; 37 | static constexpr size_t DateTimeAlignment = 4; 38 | 39 | static constexpr size_t BatteryImplSize = 48; 40 | static constexpr size_t BatteryAlignment = 8; 41 | #endif 42 | }; // namespace Platform -------------------------------------------------------------------------------- /Firmware/utils/inc/utils/coroutine/Common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef _MSC_VER 11 | #include 12 | #include 13 | namespace stdcoro = std; 14 | #elif __GNUC__ 15 | #include 16 | namespace stdcoro = std; 17 | #else 18 | #include 19 | namespace stdcoro = std::experimental 20 | #endif 21 | 22 | #include 23 | #include 24 | 25 | struct Promise 26 | { 27 | auto initial_suspend() noexcept 28 | { 29 | return stdcoro::suspend_never{}; 30 | } 31 | auto final_suspend() noexcept 32 | { 33 | return stdcoro::suspend_never{}; 34 | } 35 | 36 | void get_return_object() 37 | { 38 | } 39 | void return_void() 40 | { 41 | } 42 | 43 | void unhandled_exception() 44 | { 45 | while (1) 46 | { 47 | } 48 | } 49 | }; 50 | 51 | template struct stdcoro::coroutine_traits 52 | { 53 | using promise_type = Promise; 54 | }; -------------------------------------------------------------------------------- /Firmware/utils/inc/utils/coroutine/Event.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Common.hpp" 3 | #include "ExecutionQueueCoro.hpp" 4 | 5 | namespace CoroUtils 6 | { 7 | 8 | struct Event 9 | { 10 | Event(bool isSet = false) : m_isSet{isSet} 11 | { 12 | } 13 | 14 | bool await_ready() noexcept 15 | { 16 | return m_isSet; 17 | } 18 | 19 | void await_suspend(stdcoro::coroutine_handle<> handle) 20 | { 21 | m_continuation = handle; 22 | } 23 | 24 | void await_resume() 25 | { 26 | } 27 | 28 | void set(bool toMainThread = false) 29 | { 30 | m_isSet.store(true); 31 | if (m_continuation && !m_continuation.done()) 32 | { 33 | if (toMainThread) 34 | { 35 | CoroQueueMainLoop::GetInstance().pushToLater(m_continuation); 36 | } 37 | else 38 | { 39 | m_continuation.resume(); 40 | } 41 | } 42 | } 43 | 44 | bool isSet() const noexcept 45 | { 46 | return m_isSet; 47 | } 48 | 49 | std::atomic_bool m_isSet; 50 | stdcoro::coroutine_handle<> m_continuation; 51 | }; 52 | 53 | } // namespace CoroUtils -------------------------------------------------------------------------------- /Firmware/utils/inc/utils/coroutine/ExecutionQueueCoro.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Common.hpp" 3 | 4 | namespace CoroUtils 5 | { 6 | 7 | struct CoroQueueMainLoop 8 | { 9 | static CoroQueueMainLoop& GetInstance() 10 | { 11 | static CoroQueueMainLoop instance{}; 12 | return instance; 13 | } 14 | 15 | void pushToLater(std::coroutine_handle<> coroHandle) 16 | { 17 | executionQueue.push(coroHandle); 18 | } 19 | 20 | void processQueue() 21 | { 22 | std::coroutine_handle<> handle; 23 | while (executionQueue.pop(handle)) 24 | { 25 | if (!handle.done()) 26 | handle.resume(); 27 | } 28 | } 29 | 30 | template 31 | using TQueueStorageType = 32 | etl::queue_spsc_atomic; 33 | 34 | TQueueStorageType> executionQueue; 35 | }; 36 | 37 | } // namespace CoroUtils -------------------------------------------------------------------------------- /Firmware/utils/inc/utils/coroutine/WhenAllSequence.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Common.hpp" 3 | 4 | namespace CoroUtils 5 | { 6 | 7 | template struct WhenAllSequence 8 | { 9 | std::tuple m_taskList; 10 | 11 | explicit WhenAllSequence(Tasks&&... tasks) noexcept : m_taskList(std::move(tasks)...) 12 | { 13 | } 14 | 15 | explicit WhenAllSequence(std::tuple&& tasks) noexcept : m_taskList(std::move(tasks)) 16 | { 17 | } 18 | 19 | bool await_ready() const noexcept 20 | { 21 | return false; 22 | } 23 | 24 | void await_suspend(stdcoro::coroutine_handle<> handle) noexcept 25 | { 26 | co_await launchAll(std::make_integer_sequence{}); 27 | handle.resume(); 28 | } 29 | 30 | template 31 | VoidTask launchAll(std::integer_sequence) 32 | { 33 | (co_await std::get(m_taskList), ...); 34 | } 35 | 36 | void await_resume() noexcept 37 | { 38 | } 39 | }; 40 | 41 | template auto when_all_sequence(Args&&... args) noexcept 42 | { 43 | return WhenAllSequence{std::make_tuple(std::move(args)...)}; 44 | } 45 | 46 | } // namespace CoroUtils -------------------------------------------------------------------------------- /Images/1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Images/Design/DesignThreePanels.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/Design/DesignThreePanels.psd -------------------------------------------------------------------------------- /Images/Design/PlayerIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/Design/PlayerIcons.ttf -------------------------------------------------------------------------------- /Images/Design/a_LCDNova.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/Design/a_LCDNova.ttf -------------------------------------------------------------------------------- /Images/Design/v1.1_Render/HealthPage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/Design/v1.1_Render/HealthPage.jpg -------------------------------------------------------------------------------- /Images/Design/v1.1_Render/HealthPageLight.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/Design/v1.1_Render/HealthPageLight.jpg -------------------------------------------------------------------------------- /Images/Design/v1.1_Render/HealthPageLightCombined.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/Design/v1.1_Render/HealthPageLightCombined.jpg -------------------------------------------------------------------------------- /Images/Design/v1.1_Render/MainPage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/Design/v1.1_Render/MainPage.jpg -------------------------------------------------------------------------------- /Images/Design/v1.1_Render/MainPageLight.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/Design/v1.1_Render/MainPageLight.jpg -------------------------------------------------------------------------------- /Images/Design/v1.1_Render/MusicPage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/Design/v1.1_Render/MusicPage.jpg -------------------------------------------------------------------------------- /Images/Design/v1.1_Render/MusicPageLight.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/Design/v1.1_Render/MusicPageLight.jpg -------------------------------------------------------------------------------- /Images/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 56 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Images/arrow_right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 56 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Images/c4d8c1d4-cd41-4379-84de-3c51f9422b52.jfif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/c4d8c1d4-cd41-4379-84de-3c51f9422b52.jfif -------------------------------------------------------------------------------- /Images/d303f735-42f6-4d54-85bb-543812d70a62.jfif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/d303f735-42f6-4d54-85bb-543812d70a62.jfif -------------------------------------------------------------------------------- /Images/line.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 56 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Images/photo_2019-11-03_00-30-05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/photo_2019-11-03_00-30-05.jpg -------------------------------------------------------------------------------- /Images/photo_2019-11-03_00-31-58.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/photo_2019-11-03_00-31-58.jpg -------------------------------------------------------------------------------- /Images/photo_2020-01-09_01-23-37.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/photo_2020-01-09_01-23-37.jpg -------------------------------------------------------------------------------- /Images/v1.1/photo_2020-01-25_01-45-46.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/v1.1/photo_2020-01-25_01-45-46.jpg -------------------------------------------------------------------------------- /Images/v1.1/photo_2020-03-02_02-10-26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/v1.1/photo_2020-03-02_02-10-26.jpg -------------------------------------------------------------------------------- /Images/v1.1/photo_2020-03-02_13-30-43.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/v1.1/photo_2020-03-02_13-30-43.jpg -------------------------------------------------------------------------------- /Images/v1.1/photo_2020-04-12_14-58-47.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/v1.1/photo_2020-04-12_14-58-47.jpg -------------------------------------------------------------------------------- /Images/v1.1/photo_2020-04-12_14-58-50.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/v1.1/photo_2020-04-12_14-58-50.jpg -------------------------------------------------------------------------------- /Images/v1.1/photo_2020-04-12_14-58-51.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/v1.1/photo_2020-04-12_14-58-51.jpg -------------------------------------------------------------------------------- /Images/v1.2/WatchBorad-Bottom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/v1.2/WatchBorad-Bottom.jpg -------------------------------------------------------------------------------- /Images/v1.2/WatchBorad-top.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/v1.2/WatchBorad-top.jpg -------------------------------------------------------------------------------- /Images/Снимок.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/Снимок.JPG -------------------------------------------------------------------------------- /Images/Снимок1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Images/Снимок1.JPG -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Valentyn Korniienko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GradWork 2 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/08c0d7971b704b748f2d73a1324b52d2)](https://www.codacy.com/manual/kornienko-vr/GradWork?utm_source=github.com&utm_medium=referral&utm_content=ValentiWorkLearning/GradWork&utm_campaign=Badge_Grade)![CI](https://github.com/ValentiWorkLearning/GradWork/workflows/CI/badge.svg?branch=dev%2Fdevelop) 3 | 4 | Development board for smart-handwatch based on NRF52832 E73 module with C++20/CMake/VSCode based development flow 5 | 6 | * Uses the C++20 features as wide as possible 7 | * Uses С++20 coroutines for non-blocking interactions 8 | * Port of LVGL 8.0 library for ST7789V/GC9A01 display and NRFSDK 9 | * Simulator for fimware development without target MCU 10 | * CI for Windows Simulator build and GCC build for target 11 | 12 | ### Article: 13 | [C++20 Coroutines on NRF52832 with GTest](https://habr.com/ru/post/566070/) 14 | 15 | ### Revision v1.2 16 | ![alt text](https://github.com/ValentiWorkLearning/GradWork/blob/master/Images/v1.2/WatchBorad-top.jpg "Revision v1.2") 17 | 18 | ![alt text](https://github.com/ValentiWorkLearning/GradWork/blob/master/Images/v1.2/WatchBorad-Bottom.jpg "Revision v1.2") 19 | 20 | ### Revision v1.0 21 | ![alt text](https://github.com/ValentiWorkLearning/GradWork/blob/master/Images/v1.1/photo_2020-04-12_14-58-51.jpg "Revision v1.0") 22 | 23 | ![alt text](https://github.com/ValentiWorkLearning/GradWork/blob/master/Images/v1.1/photo_2020-04-12_14-58-50.jpg "Revision v1.0") 24 | 25 | ![alt text](https://github.com/ValentiWorkLearning/GradWork/blob/master/Images/v1.1/photo_2020-04-12_14-58-47.jpg "Revision v1.0") 26 | -------------------------------------------------------------------------------- /Schematic/Display_devboard_schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Schematic/Display_devboard_schematic.pdf -------------------------------------------------------------------------------- /Schematic/MissedParts.md: -------------------------------------------------------------------------------- 1 | 1. D_TVS 2 | https://lcsc.com/product-detail/TVS_PN-SILICON-PESDU0521P1_C499647.html 3 | * Replacement: https://lcsc.com/product-detail/TVS_Leiditech-ESDA05CP_C384839.html 4 | 5 | 2. Schottky Barrier 6 | https://lcsc.com/product-detail/Schottky-Barrier-Diodes-SBD_Shandong-Jingdao-Microelectronics-SS12_C353243.html 7 | * Replacement: https://lcsc.com/product-detail/Schottky-Barrier-Diodes-SBD_Slkor-SLKORMICRO-Elec-SS12_C513476.html 8 | 9 | 10 | 3. Switches 11 | https://lcsc.com/products/Tactile-Switches_427.html?q=b3u-3000 12 | * Replacement: https://lcsc.com/product-detail/Tactile-Switches_Omron-Electronics-B3U-3000P_C963349.html 13 | 14 | 4. BNO055 15 | https://lcsc.com/product-detail/Sensors_Bosch-Sensortec_BNO055_Bosch-Sensortec-BNO055_C93216.html 16 | * Replacement ? 17 | - https://www.digikey.com/en/products/detail/bosch-sensortec/BHI160B/9674245 18 | - https://lcsc.com/product-detail/Motion-Sensors-Accelerometers_Bosch-Sensortec-BHI160B_C464452.html 19 | - https://github.com/BoschSensortec/BMA400-API 20 | 21 | 5. FLASH Memory: 22 | 23 | * Replacement: 24 | https://lcsc.com/product-detail/FLASH_Winbond-Elec-W25Q128JVPIQTR_C190862.html 25 | 26 | 6. BMS: 27 | 28 | https://lcsc.com/product-detail/_STMicroelectronics_STC3100IQT_STC3100IQT_C155587.html 29 | 30 | 7. Switching controllers: 31 | 32 | https://lcsc.com/product-detail/Switching-Controllers_Texas-Instruments_TPS2115APWR_Texas-Instruments-Texas-Instruments-TPS2115APWR_C70287.html 33 | 34 | 8. Supervisors 35 | 36 | https://lcsc.com/product-detail/Microprocessor-Microcontroller-Supervisors_ON-Semiconductor-ON-Semiconductor-MAX809STRG_C9965.html 37 | 38 | 9. DC-DC Converter 39 | 40 | https://lcsc.com/product-detail/DC-DC-Converters_Texas-Instruments_TPS62740DSSR_Texas-Instruments-Texas-Instruments-TPS62740DSSR_C128606.html 41 | 42 | 10. 32768crystal 43 | 44 | https://lcsc.com/product-detail/SMD-Crystal-Resonators_Seiko_SC-20S-32-768kHz-20PPM-7pF_Seiko-SC-20S-32-768kHz-20PPM-7pF_C97602.html 45 | 46 | -------------------------------------------------------------------------------- /Schematic/WatchBorad/DCDC_FG_BCH_PB.sch: -------------------------------------------------------------------------------- 1 | EESchema Schematic File Version 4 2 | EELAYER 30 0 3 | EELAYER END 4 | $Descr A4 11693 8268 5 | encoding utf-8 6 | Sheet 4 6 7 | Title "" 8 | Date "" 9 | Rev "" 10 | Comp "" 11 | Comment1 "" 12 | Comment2 "" 13 | Comment3 "" 14 | Comment4 "" 15 | $EndDescr 16 | $EndSCHEMATC 17 | -------------------------------------------------------------------------------- /Schematic/WatchBorad/VibrationMotor.sch: -------------------------------------------------------------------------------- 1 | EESchema Schematic File Version 4 2 | EELAYER 30 0 3 | EELAYER END 4 | $Descr A4 11693 8268 5 | encoding utf-8 6 | Sheet 2 6 7 | Title "" 8 | Date "" 9 | Rev "" 10 | Comp "" 11 | Comment1 "" 12 | Comment2 "" 13 | Comment3 "" 14 | Comment4 "" 15 | $EndDescr 16 | $EndSCHEMATC 17 | -------------------------------------------------------------------------------- /Schematic/WatchBorad/WatchBoard.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValentiWorkLearning/GradWork/801f99277986aba1f3930cf73d7f1bdf372cd37e/Schematic/WatchBorad/WatchBoard.pdf -------------------------------------------------------------------------------- /Schematic/WatchBorad/WatchBorad.sch: -------------------------------------------------------------------------------- 1 | EESchema Schematic File Version 4 2 | EELAYER 30 0 3 | EELAYER END 4 | $Descr A3 16535 11693 5 | encoding utf-8 6 | Sheet 1 4 7 | Title "" 8 | Date "" 9 | Rev "" 10 | Comp "" 11 | Comment1 "" 12 | Comment2 "" 13 | Comment3 "" 14 | Comment4 "" 15 | $EndDescr 16 | Wire Notes Line 17 | 600 6300 600 7700 18 | Wire Notes Line 19 | 575 7775 575 8825 20 | $Sheet 21 | S 8000 4500 750 1675 22 | U 5E9B1DCE 23 | F0 "MCU" 50 24 | F1 "MCU.sch" 50 25 | F2 "PB_1_MCU" I R 8750 4675 50 26 | F3 "PB_2_MCU" I R 8750 4750 50 27 | F4 "PB_3_MCU" I R 8750 5800 50 28 | F5 "PB_4_MCU" I R 8750 5875 50 29 | F6 "PB_5_MCU" I R 8750 5950 50 30 | F7 "Enable_motor" O R 8750 6075 50 31 | F8 "V3V3" I L 8000 5950 50 32 | F9 "SDA_FG" B R 8750 4900 50 33 | F10 "SCL_FG" I R 8750 4975 50 34 | F11 "FG_OD_ALRT_N" I R 8750 5050 50 35 | $EndSheet 36 | $Sheet 37 | S 9375 4500 1050 725 38 | U 5F913559 39 | F0 "PSU_logic" 50 40 | F1 "PSU_logic.sch" 50 41 | F2 "PB_1_MCU" O L 9375 4675 50 42 | F3 "PB_2_MCU" O L 9375 4750 50 43 | F4 "V_OUT_MUX" O L 9375 5175 50 44 | F5 "SDA_FG" B L 9375 4900 50 45 | F6 "SCL_FG" I L 9375 4975 50 46 | F7 "FG_OD_ALRT_N" O L 9375 5050 50 47 | $EndSheet 48 | $Sheet 49 | S 9375 5550 1050 575 50 | U 5F988DA5 51 | F0 "DCDC&PB" 50 52 | F1 "DCDC&PB.sch" 50 53 | F2 "V3V3" O R 10425 5675 50 54 | F3 "PB_3_MCU_N" O L 9375 5800 50 55 | F4 "PB_4_MCU_N" O L 9375 5875 50 56 | F5 "PB_5_MCU_N" O L 9375 5950 50 57 | F6 "Enable_motor" I L 9375 6075 50 58 | F7 "DCDC_VIN" I L 9375 5650 50 59 | $EndSheet 60 | Wire Wire Line 61 | 9375 5175 9325 5175 62 | Wire Wire Line 63 | 9325 5175 9325 5650 64 | Wire Wire Line 65 | 9325 5650 9375 5650 66 | Wire Wire Line 67 | 8000 5950 7900 5950 68 | Wire Wire Line 69 | 7900 5950 7900 6400 70 | Wire Wire Line 71 | 7900 6400 10650 6400 72 | Wire Wire Line 73 | 10650 6400 10650 5675 74 | Wire Wire Line 75 | 10650 5675 10425 5675 76 | Wire Wire Line 77 | 8750 5950 9375 5950 78 | Wire Wire Line 79 | 8750 5875 9375 5875 80 | Wire Wire Line 81 | 8750 6075 9375 6075 82 | Wire Wire Line 83 | 8750 5800 9375 5800 84 | Wire Wire Line 85 | 8750 4675 9375 4675 86 | Wire Wire Line 87 | 9375 4750 8750 4750 88 | Wire Wire Line 89 | 8750 4975 9375 4975 90 | Wire Wire Line 91 | 9375 4900 8750 4900 92 | Wire Wire Line 93 | 8750 5050 9375 5050 94 | $EndSCHEMATC 95 | -------------------------------------------------------------------------------- /Schematic/WatchBorad/fp-lib-table: -------------------------------------------------------------------------------- 1 | (fp_lib_table 2 | (lib (name FH26W-13S-0)(type KiCad)(uri ${KICAD_LIBRARIES_ROOT}/FH26W-13S-0.3SHW_60)(options "")(descr "")) 3 | (lib (name MPU-9250)(type Legacy)(uri ${KICAD_LIBRARIES_ROOT}/MPU-9250/MPU-9250.mod)(options "")(descr "")) 4 | (lib (name W25Q16BVSSIG)(type Legacy)(uri ${KICAD_LIBRARIES_ROOT}/W25Q16BVSSIG/W25Q16BVSSIG.mod)(options "")(descr "")) 5 | (lib (name TXS0108E)(type Legacy)(uri ${KICAD_LIBRARIES_ROOT}/TXS0108E/TXS0108E.mod)(options "")(descr "")) 6 | (lib (name BU6655NUX-TR)(type Legacy)(uri ${KICAD_LIBRARIES_ROOT}/BU6655NUX-TR/BU6655NUX-TR.mod)(options "")(descr "")) 7 | (lib (name testpoints)(type KiCad)(uri ${KICAD_LIBRARIES_ROOT}/testpoints/testpoints.pretty)(options "")(descr "")) 8 | (lib (name LocalFootprints)(type KiCad)(uri ${KICAD_LIBRARIES_ROOT}/Footprints)(options "")(descr "")) 9 | (lib (name USB)(type KiCad)(uri ${KICAD_LIBRARIES_ROOT}/USB_connectors)(options "")(descr "")) 10 | (lib (name Footprints)(type KiCad)(uri ${KICAD_LIBRARIES_ROOT}/Footprints.pretty)(options "")(descr "")) 11 | (lib (name OmronSwitch)(type KiCad)(uri ${KICAD_LIBRARIES_ROOT}/B3U-3000P)(options "")(descr "")) 12 | (lib (name M41T62Q6F)(type KiCad)(uri ${KICAD_LIBRARIES_ROOT}/M41T62Q6F)(options "")(descr "")) 13 | (lib (name TPS73501DRBR)(type KiCad)(uri ${KICAD_LIBRARIES_ROOT}/TPS73501DRBR/)(options "")(descr "")) 14 | (lib (name ANT2012LL13R2400A)(type KiCad)(uri ${KICAD_LIBRARIES_ROOT}/YAGEO_RF/ANT2012LL13R2400A.pretty)(options "")(descr "")) 15 | (lib (name LSM6DSRTR)(type KiCad)(uri ${KICAD_LIBRARIES_ROOT}/LSM6DSRTR/)(options "")(descr "")) 16 | (lib (name SN74LVC2G74)(type KiCad)(uri ${KICAD_LIBRARIES_ROOT}/SN74LVC2G74/)(options "")(descr "")) 17 | ) 18 | --------------------------------------------------------------------------------