From 118ff99b5b5fdc1aee472f9be3b1c709a55fc240 Mon Sep 17 00:00:00 2001 From: Forairaaaaa Date: Mon, 11 May 2026 17:28:48 +0800 Subject: [PATCH] init --- .github/ISSUE_TEMPLATE/bug-report.yml | 2 +- .github/workflows/Arduino-Lint-Check.yml | 27 - .github/workflows/clang-format-check.yml | 4 +- .gitignore | 4 + CMakeLists.txt | 6 + README.md | 56 +- dependencies.lock | 58 + fetch_repos.py | 61 + main/CMakeLists.txt | 27 + main/apps/app_alarm_clock/app_alarm_clock.cpp | 128 + main/apps/app_alarm_clock/app_alarm_clock.h | 34 + .../app_alarm_clock/model/alarm_clock.cpp | 239 + main/apps/app_alarm_clock/model/alarm_clock.h | 82 + main/apps/app_alarm_clock/view/add_alarm.cpp | 131 + main/apps/app_alarm_clock/view/alarm_list.cpp | 263 + .../app_alarm_clock/view/trigger_alarm.cpp | 73 + main/apps/app_alarm_clock/view/view.h | 104 + main/apps/app_badge/app_badge.cpp | 98 + main/apps/app_badge/app_badge.h | 29 + main/apps/app_badge/view/badge.cpp | 159 + main/apps/app_badge/view/view.h | 57 + main/apps/app_fft/app_fft.cpp | 66 + main/apps/app_fft/app_fft.h | 28 + main/apps/app_fft/view/view.cpp | 381 + main/apps/app_fft/view/view.h | 76 + main/apps/app_imu/app_imu.cpp | 92 + main/apps/app_imu/app_imu.h | 31 + main/apps/app_imu/view/view.cpp | 330 + main/apps/app_imu/view/view.h | 50 + main/apps/app_launcher/app_launcher.cpp | 139 + main/apps/app_launcher/app_launcher.h | 32 + main/apps/app_launcher/view/guide_page.cpp | 29 + main/apps/app_launcher/view/view.cpp | 471 + main/apps/app_launcher/view/view.h | 68 + main/apps/app_lucky_wheel/app_lucky_wheel.cpp | 89 + main/apps/app_lucky_wheel/app_lucky_wheel.h | 30 + main/apps/app_lucky_wheel/view/selection.cpp | 93 + main/apps/app_lucky_wheel/view/view.h | 72 + main/apps/app_lucky_wheel/view/wheel.cpp | 289 + main/apps/app_setup/app_setup.cpp | 138 + main/apps/app_setup/app_setup.h | 37 + main/apps/app_setup/view/view.cpp | 92 + main/apps/app_setup/view/view.h | 43 + main/apps/app_setup/workers/about.cpp | 113 + main/apps/app_setup/workers/datetime.cpp | 376 + main/apps/app_setup/workers/device.cpp | 307 + main/apps/app_setup/workers/workers.h | 141 + main/apps/app_stopwatch/app_stopwatch.cpp | 60 + main/apps/app_stopwatch/app_stopwatch.h | 29 + main/apps/app_stopwatch/model/stopwatch.cpp | 82 + main/apps/app_stopwatch/model/stopwatch.h | 49 + main/apps/app_stopwatch/view/view.cpp | 256 + main/apps/app_stopwatch/view/view.h | 59 + main/apps/app_template/app_template.cpp | 87 + main/apps/app_template/app_template.h | 27 + main/apps/app_watch_face/app_watch_face.cpp | 77 + main/apps/app_watch_face/app_watch_face.h | 29 + main/apps/app_watch_face/view/big_number.cpp | 178 + main/apps/app_watch_face/view/classic.cpp | 296 + main/apps/app_watch_face/view/number_flow.cpp | 224 + main/apps/app_watch_face/view/simple.cpp | 289 + main/apps/app_watch_face/view/watch_face.h | 156 + .../view/watch_face_manager.cpp | 160 + main/apps/apps.h | 16 + .../common/arc_top_clock/arc_top_clock.cpp | 66 + .../apps/common/arc_top_clock/arc_top_clock.h | 37 + main/apps/common/audio/audio.cpp | 141 + main/apps/common/audio/audio.h | 20 + main/apps/common/audio/common.h | 13 + main/apps/common/common.h | 13 + main/apps/common/key_manager/key_manager.cpp | 32 + main/apps/common/key_manager/key_manager.h | 31 + main/apps/common/loading_page/loading_page.h | 44 + main/apps/common/status_bar/status_bar.cpp | 381 + main/apps/common/status_bar/status_bar.h | 20 + main/assets/assets.h | 45 + main/assets/fonts/CommissionerMedium108.c | 747 ++ main/assets/fonts/CommissionerMedium64.c | 375 + main/assets/fonts/MontserratSemiBold26.c | 717 + .../fonts/lv_font_maple_mono_medium_24.c | 649 + .../fonts/lv_font_maple_mono_medium_28.c | 730 + .../fonts/lv_font_maple_mono_medium_48.c | 1248 ++ main/assets/fonts/symbols.txt | 1 + main/assets/images/alarm_icon.c | 651 + main/assets/images/go_home_guide.c | 11007 ++++++++++++++++ main/assets/images/icon_badge.c | 4244 ++++++ main/assets/images/icon_bat_lightning.c | 68 + main/assets/images/icon_clock.c | 4244 ++++++ main/assets/images/icon_edit_badge.c | 206 + main/assets/images/icon_fft.c | 4244 ++++++ main/assets/images/icon_imu.c | 4244 ++++++ main/assets/images/icon_indicator_left.c | 349 + main/assets/images/icon_indicator_right.c | 349 + main/assets/images/icon_lucky_wheel.c | 4479 +++++++ main/assets/images/icon_setup.c | 4244 ++++++ main/assets/images/icon_stopwatch.c | 4244 ++++++ main/assets/images/icon_watch_face.c | 4479 +++++++ main/assets/images/lucky_wheel_pointer.c | 1931 +++ .../watch_face/big_number/big_number_0.c | 2688 ++++ .../watch_face/big_number/big_number_1.c | 2688 ++++ .../watch_face/big_number/big_number_2.c | 2688 ++++ .../watch_face/big_number/big_number_3.c | 2688 ++++ .../watch_face/big_number/big_number_4.c | 2688 ++++ .../watch_face/big_number/big_number_5.c | 2688 ++++ .../watch_face/big_number/big_number_6.c | 2688 ++++ .../watch_face/big_number/big_number_7.c | 2688 ++++ .../watch_face/big_number/big_number_8.c | 2688 ++++ .../watch_face/big_number/big_number_9.c | 2688 ++++ .../watch_face/classic/classic_hour_hand.c | 329 + .../watch_face/classic/classic_minute_hand.c | 289 + .../watch_face/classic/classic_second_hand.c | 596 + main/assets/sfx/boot_sfx.bin | Bin 0 -> 158760 bytes main/hal/drivers/cst820/cst820.cpp | 109 + main/hal/drivers/cst820/cst820.h | 77 + main/hal/drivers/rx8130/rx8130.cpp | 198 + main/hal/drivers/rx8130/rx8130.h | 25 + main/hal/hal.cpp | 181 + main/hal/hal.h | 306 + main/hal/hal_alarm.cpp | 250 + main/hal/hal_audio.cpp | 521 + main/hal/hal_badge.cpp | 508 + main/hal/hal_button.cpp | 80 + main/hal/hal_display.cpp | 409 + main/hal/hal_fs.cpp | 17 + main/hal/hal_imu.cpp | 48 + main/hal/hal_ioe.cpp | 239 + main/hal/hal_pmic.cpp | 167 + main/hal/hal_rtc.cpp | 211 + main/hal/utils/button/Button_Class.cpp | 70 + main/hal/utils/button/Button_Class.hpp | 154 + .../config_ap/assets/badge_config_ap.html | 764 ++ main/hal/utils/config_ap/config_ap.cpp | 585 + main/hal/utils/config_ap/config_ap.h | 56 + main/hal/utils/settings/settings.cc | 148 + main/hal/utils/settings/settings.h | 32 + .../hal/utils/wear_levelling/wear_levelling.c | 138 + .../hal/utils/wear_levelling/wear_levelling.h | 20 + main/idf_component.yml | 19 + main/main.cpp | 48 + partitions.csv | 9 + patches/M5IOE1.patch | 30 + patches/M5PM1.patch | 20 + repos.json | 50 + sdkconfig.defaults | 32 + 144 files changed, 95168 insertions(+), 69 deletions(-) delete mode 100644 .github/workflows/Arduino-Lint-Check.yml create mode 100644 CMakeLists.txt create mode 100644 dependencies.lock create mode 100644 fetch_repos.py create mode 100644 main/CMakeLists.txt create mode 100644 main/apps/app_alarm_clock/app_alarm_clock.cpp create mode 100644 main/apps/app_alarm_clock/app_alarm_clock.h create mode 100644 main/apps/app_alarm_clock/model/alarm_clock.cpp create mode 100644 main/apps/app_alarm_clock/model/alarm_clock.h create mode 100644 main/apps/app_alarm_clock/view/add_alarm.cpp create mode 100644 main/apps/app_alarm_clock/view/alarm_list.cpp create mode 100644 main/apps/app_alarm_clock/view/trigger_alarm.cpp create mode 100644 main/apps/app_alarm_clock/view/view.h create mode 100644 main/apps/app_badge/app_badge.cpp create mode 100644 main/apps/app_badge/app_badge.h create mode 100644 main/apps/app_badge/view/badge.cpp create mode 100644 main/apps/app_badge/view/view.h create mode 100644 main/apps/app_fft/app_fft.cpp create mode 100644 main/apps/app_fft/app_fft.h create mode 100644 main/apps/app_fft/view/view.cpp create mode 100644 main/apps/app_fft/view/view.h create mode 100644 main/apps/app_imu/app_imu.cpp create mode 100644 main/apps/app_imu/app_imu.h create mode 100644 main/apps/app_imu/view/view.cpp create mode 100644 main/apps/app_imu/view/view.h create mode 100644 main/apps/app_launcher/app_launcher.cpp create mode 100644 main/apps/app_launcher/app_launcher.h create mode 100644 main/apps/app_launcher/view/guide_page.cpp create mode 100644 main/apps/app_launcher/view/view.cpp create mode 100644 main/apps/app_launcher/view/view.h create mode 100644 main/apps/app_lucky_wheel/app_lucky_wheel.cpp create mode 100644 main/apps/app_lucky_wheel/app_lucky_wheel.h create mode 100644 main/apps/app_lucky_wheel/view/selection.cpp create mode 100644 main/apps/app_lucky_wheel/view/view.h create mode 100644 main/apps/app_lucky_wheel/view/wheel.cpp create mode 100644 main/apps/app_setup/app_setup.cpp create mode 100644 main/apps/app_setup/app_setup.h create mode 100644 main/apps/app_setup/view/view.cpp create mode 100644 main/apps/app_setup/view/view.h create mode 100644 main/apps/app_setup/workers/about.cpp create mode 100644 main/apps/app_setup/workers/datetime.cpp create mode 100644 main/apps/app_setup/workers/device.cpp create mode 100644 main/apps/app_setup/workers/workers.h create mode 100644 main/apps/app_stopwatch/app_stopwatch.cpp create mode 100644 main/apps/app_stopwatch/app_stopwatch.h create mode 100644 main/apps/app_stopwatch/model/stopwatch.cpp create mode 100644 main/apps/app_stopwatch/model/stopwatch.h create mode 100644 main/apps/app_stopwatch/view/view.cpp create mode 100644 main/apps/app_stopwatch/view/view.h create mode 100644 main/apps/app_template/app_template.cpp create mode 100644 main/apps/app_template/app_template.h create mode 100644 main/apps/app_watch_face/app_watch_face.cpp create mode 100644 main/apps/app_watch_face/app_watch_face.h create mode 100644 main/apps/app_watch_face/view/big_number.cpp create mode 100644 main/apps/app_watch_face/view/classic.cpp create mode 100644 main/apps/app_watch_face/view/number_flow.cpp create mode 100644 main/apps/app_watch_face/view/simple.cpp create mode 100644 main/apps/app_watch_face/view/watch_face.h create mode 100644 main/apps/app_watch_face/view/watch_face_manager.cpp create mode 100644 main/apps/apps.h create mode 100644 main/apps/common/arc_top_clock/arc_top_clock.cpp create mode 100644 main/apps/common/arc_top_clock/arc_top_clock.h create mode 100644 main/apps/common/audio/audio.cpp create mode 100644 main/apps/common/audio/audio.h create mode 100644 main/apps/common/audio/common.h create mode 100644 main/apps/common/common.h create mode 100644 main/apps/common/key_manager/key_manager.cpp create mode 100644 main/apps/common/key_manager/key_manager.h create mode 100644 main/apps/common/loading_page/loading_page.h create mode 100644 main/apps/common/status_bar/status_bar.cpp create mode 100644 main/apps/common/status_bar/status_bar.h create mode 100644 main/assets/assets.h create mode 100644 main/assets/fonts/CommissionerMedium108.c create mode 100644 main/assets/fonts/CommissionerMedium64.c create mode 100644 main/assets/fonts/MontserratSemiBold26.c create mode 100644 main/assets/fonts/lv_font_maple_mono_medium_24.c create mode 100644 main/assets/fonts/lv_font_maple_mono_medium_28.c create mode 100644 main/assets/fonts/lv_font_maple_mono_medium_48.c create mode 100644 main/assets/fonts/symbols.txt create mode 100644 main/assets/images/alarm_icon.c create mode 100644 main/assets/images/go_home_guide.c create mode 100644 main/assets/images/icon_badge.c create mode 100644 main/assets/images/icon_bat_lightning.c create mode 100644 main/assets/images/icon_clock.c create mode 100644 main/assets/images/icon_edit_badge.c create mode 100644 main/assets/images/icon_fft.c create mode 100644 main/assets/images/icon_imu.c create mode 100644 main/assets/images/icon_indicator_left.c create mode 100644 main/assets/images/icon_indicator_right.c create mode 100644 main/assets/images/icon_lucky_wheel.c create mode 100644 main/assets/images/icon_setup.c create mode 100644 main/assets/images/icon_stopwatch.c create mode 100644 main/assets/images/icon_watch_face.c create mode 100644 main/assets/images/lucky_wheel_pointer.c create mode 100644 main/assets/images/watch_face/big_number/big_number_0.c create mode 100644 main/assets/images/watch_face/big_number/big_number_1.c create mode 100644 main/assets/images/watch_face/big_number/big_number_2.c create mode 100644 main/assets/images/watch_face/big_number/big_number_3.c create mode 100644 main/assets/images/watch_face/big_number/big_number_4.c create mode 100644 main/assets/images/watch_face/big_number/big_number_5.c create mode 100644 main/assets/images/watch_face/big_number/big_number_6.c create mode 100644 main/assets/images/watch_face/big_number/big_number_7.c create mode 100644 main/assets/images/watch_face/big_number/big_number_8.c create mode 100644 main/assets/images/watch_face/big_number/big_number_9.c create mode 100644 main/assets/images/watch_face/classic/classic_hour_hand.c create mode 100644 main/assets/images/watch_face/classic/classic_minute_hand.c create mode 100644 main/assets/images/watch_face/classic/classic_second_hand.c create mode 100644 main/assets/sfx/boot_sfx.bin create mode 100644 main/hal/drivers/cst820/cst820.cpp create mode 100644 main/hal/drivers/cst820/cst820.h create mode 100644 main/hal/drivers/rx8130/rx8130.cpp create mode 100644 main/hal/drivers/rx8130/rx8130.h create mode 100644 main/hal/hal.cpp create mode 100644 main/hal/hal.h create mode 100644 main/hal/hal_alarm.cpp create mode 100644 main/hal/hal_audio.cpp create mode 100644 main/hal/hal_badge.cpp create mode 100644 main/hal/hal_button.cpp create mode 100644 main/hal/hal_display.cpp create mode 100644 main/hal/hal_fs.cpp create mode 100644 main/hal/hal_imu.cpp create mode 100644 main/hal/hal_ioe.cpp create mode 100644 main/hal/hal_pmic.cpp create mode 100644 main/hal/hal_rtc.cpp create mode 100644 main/hal/utils/button/Button_Class.cpp create mode 100644 main/hal/utils/button/Button_Class.hpp create mode 100644 main/hal/utils/config_ap/assets/badge_config_ap.html create mode 100644 main/hal/utils/config_ap/config_ap.cpp create mode 100644 main/hal/utils/config_ap/config_ap.h create mode 100644 main/hal/utils/settings/settings.cc create mode 100644 main/hal/utils/settings/settings.h create mode 100644 main/hal/utils/wear_levelling/wear_levelling.c create mode 100644 main/hal/utils/wear_levelling/wear_levelling.h create mode 100644 main/idf_component.yml create mode 100644 main/main.cpp create mode 100644 partitions.csv create mode 100644 patches/M5IOE1.patch create mode 100644 patches/M5PM1.patch create mode 100644 repos.json create mode 100644 sdkconfig.defaults diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 594a2c58..fc47abc1 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -76,7 +76,7 @@ body: label: Issue checklist description: Please double-check that you have done each of the following things before submitting the issue. options: - - label: I searched for previous reports in [the issue tracker](https://github.com/m5stack/M5Stack/issues?q=) + - label: I searched for previous reports in [the issue tracker](https://github.com/m5stack/M5StopWatch-UserDemo/issues?q=) required: true - label: My report contains all necessary details required: true diff --git a/.github/workflows/Arduino-Lint-Check.yml b/.github/workflows/Arduino-Lint-Check.yml deleted file mode 100644 index de06bf00..00000000 --- a/.github/workflows/Arduino-Lint-Check.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Arduino Lint Check -on: - push: - branches: [ master, main ] - pull_request: - branches: [ master, main ] - workflow_dispatch: - -defaults: - run: - shell: bash - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - lint: - name: Lint Check - runs-on: [self-hosted, Linux, X64] - steps: - - uses: actions/checkout@v4 - - uses: arduino/arduino-lint-action@v2 - with: - library-manager: update - compliance: strict - project-type: all diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index dc9e340a..13c1e6a4 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -40,7 +40,7 @@ jobs: matrix: path: - check: './' # path to include - exclude: '' # path to exclude + exclude: '/(assets|hal/drivers)' # path to exclude #- check: 'src' # exclude: '(Fonts)' # Exclude file paths containing "Fonts" #- check: 'examples' @@ -55,7 +55,7 @@ jobs: - name: Run clang-format style check for C/C++/Protobuf programs. uses: jidicula/clang-format-action@v4.10.2 # Using include-regex 10.x or later with: - clang-format-version: '18' + clang-format-version: '22' check-path: ${{ matrix.path['check'] }} exclude-regex: ${{ matrix.path['exclude'] }} include-regex: ${{ env.INCLUDE_REGEX }} diff --git a/.gitignore b/.gitignore index 540f9182..6e233f0a 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ # Build files build/ cmake-build-*/ +/components/ managed_components/ # PlatformIO @@ -59,3 +60,6 @@ bin/ obj/ *.code-workspace + +sdkconfig +sdkconfig.old diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..ff7bc652 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(StopWatch-UserDemo) diff --git a/README.md b/README.md index 2be4654a..61dae9b5 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,26 @@ -# Product Name +# M5StopWatch-UserDemo +M5Stack StopWatch user demo for hardware evaluation. -## Overview +## Build -### SKU:xxx +### Fetch Dependencies -Description of the product +```bash +python3 ./fetch_repos.py +``` -## Related Link +### Tool Chains -- [Document & Datasheet](https://docs.m5stack.com/en/unit/product_Link) +[ESP-IDF v5.5.4](https://docs.espressif.com/projects/esp-idf/en/v5.5.4/esp32s3/index.html) -## Required Libraries: +### Build -- [Adafruit_BMP280_Library](https://github.com/adafruit/Required_Libraries_Link) +```bash +idf.py build +``` -## License +### Flash -- [Product Name- MIT](LICENSE) - -## Remaining steps(Editorial Staff Look,After following the steps, remember to delete all the content below) - -1. Change [clang format check path](./.github/workflows/clang-format-check.yml#L42-L47). -2. Add License content to [LICENSE](/LICENSE). -3. Change link on line 78 of [bug-report.yml](./.github/ISSUE_TEMPLATE/bug-report.yml#L79). - -```cpp -Example -# M5Unit-ENV - -## Overview - -### SKU:U001 & U001-B & U001-C - -Contains M5Stack-**UNIT ENV** series related case programs.ENV is an environmental sensor with integrated SHT30 and QMP6988 internally to detect temperature, humidity, and atmospheric pressure data. - -## Related Link - -- [Document & Datasheet](https://docs.m5stack.com/en/unit/envIII) - -## Required Libraries: - -- [Adafruit_BMP280_Library](https://github.com/adafruit/Adafruit_BMP280_Library) - -## License - -- [M5Unit-ENV - MIT](LICENSE) -``` \ No newline at end of file +```bash +idf.py flash +``` diff --git a/dependencies.lock b/dependencies.lock new file mode 100644 index 00000000..f0238655 --- /dev/null +++ b/dependencies.lock @@ -0,0 +1,58 @@ +dependencies: + espressif/cmake_utilities: + component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f + dependencies: + - name: idf + require: private + version: '>=4.1' + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.5.3 + espressif/esp-dsp: + component_hash: 12e44db246517a627bc0fe2b255b8335410c0215c98bfba2df5a8e3edea839ef + dependencies: + - name: idf + require: private + version: '>=4.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.8.0 + espressif/esp_codec_dev: + component_hash: 0d9e9bc288156eb55f79338d312e1ebf8c9dfbd5e7d13ef0f20ccb031b4e15cf + dependencies: + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.5.4 + espressif/i2c_bus: + component_hash: 4e990dc11734316186b489b362c61d41f23f79d58bc169795cec215e528cba14 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: '*' + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.5.0 + idf: + source: + type: idf + version: 5.5.4 +direct_dependencies: +- espressif/cmake_utilities +- espressif/esp-dsp +- espressif/esp_codec_dev +- espressif/i2c_bus +- idf +manifest_hash: 15747d3f6d40317f72fb137333bc3df1641a97ff034c88e9444fca5348997c95 +target: esp32s3 +version: 2.0.0 diff --git a/fetch_repos.py b/fetch_repos.py new file mode 100644 index 00000000..17cc96f7 --- /dev/null +++ b/fetch_repos.py @@ -0,0 +1,61 @@ +import os +import subprocess +import json + + +def clone_or_update_repo( + repo_url, path, ref=None, with_submodules=False, patch_path=None +): + import os + + if not os.path.exists(path): + subprocess.run(["git", "clone", repo_url, path], check=True) + else: + subprocess.run(["git", "-C", path, "fetch"], check=True) + + if ref: + subprocess.run(["git", "-C", path, "checkout", ref], check=True) + + if with_submodules: + subprocess.run( + ["git", "-C", path, "submodule", "update", "--init", "--recursive"], + check=True, + ) + + # 应用 patch + if patch_path: + patch_full_path = ( + patch_path + if os.path.isabs(patch_path) + else os.path.join(os.getcwd(), patch_path) + ) + # 使用 git apply --check 先检测补丁是否能应用,避免报错 + check_result = subprocess.run( + ["git", "-C", path, "apply", "--check", patch_full_path] + ) + if check_result.returncode == 0: + subprocess.run(["git", "-C", path, "apply", patch_full_path], check=True) + print(f"Applied patch {patch_path} to {path}") + else: + print(f"Patch {patch_path} cannot be applied cleanly to {path}, skipped.") + + +def fetch_dependencies(): + script_dir = os.path.dirname(os.path.abspath(__file__)) + config_path = os.path.join(script_dir, "repos.json") + + with open(config_path) as f: + repos = json.load(f) + + for repo in repos: + repo_path = os.path.join(script_dir, repo["path"]) + branch = repo.get("branch") + with_submodules = repo.get("with_submodules", False) + patch = repo.get("patch") + if patch and not os.path.isabs(patch): + patch = os.path.join(script_dir, patch) + clone_or_update_repo(repo["url"], repo_path, branch, with_submodules, patch) + + +if __name__ == "__main__": + fetch_dependencies() \ No newline at end of file diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 00000000..12f461c1 --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,27 @@ +file(GLOB_RECURSE MY_SRCS + "apps/*.c" + "apps/*.cc" + "apps/*.cpp" + "assets/*.c" + "assets/*.cc" + "assets/*.cpp" + "hal/*.c" + "hal/*.cc" + "hal/*.cpp" +) + +set(MY_INCS + "." +) + +idf_component_register( + SRCS + "main.cpp" + ${MY_SRCS} + INCLUDE_DIRS + ${MY_INCS} + EMBED_FILES + "assets/sfx/boot_sfx.bin" + EMBED_TXTFILES + "hal/utils/config_ap/assets/badge_config_ap.html" +) diff --git a/main/apps/app_alarm_clock/app_alarm_clock.cpp b/main/apps/app_alarm_clock/app_alarm_clock.cpp new file mode 100644 index 00000000..a80acf88 --- /dev/null +++ b/main/apps/app_alarm_clock/app_alarm_clock.cpp @@ -0,0 +1,128 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#include "app_alarm_clock.h" +#include +#include +#include +#include +#include + +using namespace mooncake; + +AppAlarmClock::AppAlarmClock() +{ + setAppInfo().name = "AlarmClock"; + setAppInfo().icon = (void*)&icon_clock; +} + +void AppAlarmClock::onCreate() +{ + mclog::tagInfo(getAppInfo().name, "on create"); + + _alarm_clock = std::make_unique(); + if (!_alarm_clock->init()) { + mclog::tagError(getAppInfo().name, "failed to init alarm clock model"); + return; + } + + _alarm_clock->onTriggered().connect([this](const model::AlarmClock::AlarmTriggeredEvent& event) { + std::unique_ptr trigger_view; + { + LvglLockGuard lock; + trigger_view = std::make_unique(); + trigger_view->init(event.time); + } + + GetHAL().startAlarm(); + + // Block until confirmed + while (1) { + GetHAL().feedTheDog(); + GetHAL().delay(100); + + LvglLockGuard lock; + if (trigger_view->isConfirmed()) { + trigger_view.reset(); + break; + } + } + + GetHAL().stopAlarm(); + }); +} + +void AppAlarmClock::onOpen() +{ + mclog::tagInfo(getAppInfo().name, "on open"); + + _key_manager = std::make_unique(); + + LvglLockGuard lock; + + if (_alarm_clock) { + _list_view = std::make_unique(*_alarm_clock); + _list_view->init(lv_screen_active()); + } +} + +void AppAlarmClock::onRunning() +{ + if (_key_manager && _key_manager->update() == input::KeyEvent::GoHome) { + close(); + return; + } + + if (_alarm_clock) { + _alarm_clock->update(); + } + + LvglLockGuard lock; + + if (_list_view) { + _list_view->update(); + + if (_list_view->consumeAddRequested()) { + _list_view.reset(); + _add_view = std::make_unique(); + _add_view->init(lv_screen_active()); + } + } + + if (_add_view && _add_view->isConfirmed() && _alarm_clock) { + auto time = _add_view->selectedTime(); + _alarm_clock->addAlarm(time, true); + + _add_view.reset(); + _list_view = std::make_unique(*_alarm_clock); + _list_view->init(lv_screen_active()); + } +} + +void AppAlarmClock::onSleeping() +{ + if (_alarm_clock) { + _alarm_clock->update(); + } +} + +void AppAlarmClock::onClose() +{ + mclog::tagInfo(getAppInfo().name, "on close"); + + _key_manager.reset(); + + LvglLockGuard lock; + + _list_view.reset(); + _add_view.reset(); +} + +void AppAlarmClock::onDestroy() +{ + mclog::tagInfo(getAppInfo().name, "on destroy"); + + _alarm_clock.reset(); +} diff --git a/main/apps/app_alarm_clock/app_alarm_clock.h b/main/apps/app_alarm_clock/app_alarm_clock.h new file mode 100644 index 00000000..9d862049 --- /dev/null +++ b/main/apps/app_alarm_clock/app_alarm_clock.h @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#pragma once +#include "model/alarm_clock.h" +#include "view/view.h" +#include +#include +#include + +/** + * @brief Derived App + * + */ +class AppAlarmClock : public mooncake::AppAbility { +public: + AppAlarmClock(); + + // Override lifecycle callbacks + void onCreate() override; + void onOpen() override; + void onRunning() override; + void onSleeping() override; + void onClose() override; + void onDestroy() override; + +private: + std::unique_ptr _key_manager; + std::unique_ptr _alarm_clock; + std::unique_ptr _list_view; + std::unique_ptr _add_view; +}; \ No newline at end of file diff --git a/main/apps/app_alarm_clock/model/alarm_clock.cpp b/main/apps/app_alarm_clock/model/alarm_clock.cpp new file mode 100644 index 00000000..7694f880 --- /dev/null +++ b/main/apps/app_alarm_clock/model/alarm_clock.cpp @@ -0,0 +1,239 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#include "alarm_clock.h" +#include +#include +#include +#include +#include + +using namespace model; + +static const std::string_view _tag = "AlarmClockModel"; + +namespace { + +std::string format_time(const AlarmClock::Time24& time) +{ + return fmt::format("{:02d}:{:02d}", time.hour, time.minute); +} + +} // namespace + +bool AlarmClock::Time24::isValid() const +{ + return hour < 24 && minute < 60; +} + +AlarmClock::Alarm::Alarm(const Time24& time, bool enabled) : _time(time), _enabled(enabled) +{ +} + +void AlarmClock::Alarm::setTime(const Time24& time) +{ + _time = time; +} + +void AlarmClock::Alarm::setEnabled(bool enabled) +{ + _enabled = enabled; +} + +bool AlarmClock::init() +{ + mclog::tagInfo(_tag, "init"); + + _alarms.clear(); + _last_update_ms = 0; + + AlarmStorageSnapshot snapshot; + if (!GetHAL().loadAlarmStorage(snapshot)) { + mclog::tagError(_tag, "load alarm storage failed"); + return false; + } + + mclog::tagInfo(_tag, "loaded storage snapshot, count: {}", snapshot.count); + + return importStorage(snapshot, false); +} + +void AlarmClock::update() +{ + uint32_t now_ms = GetHAL().millis(); + if (_last_update_ms != 0 && now_ms - _last_update_ms < 1000) { + return; + } + _last_update_ms = now_ms; + + std::time_t now = std::time(nullptr); + std::tm* local_time = std::localtime(&now); + if (local_time == nullptr) { + return; + } + + const int date_key = (local_time->tm_year + 1900) * 10000 + (local_time->tm_mon + 1) * 100 + local_time->tm_mday; + + _alarms.forEach([&](Alarm* alarm, int alarm_id) { + if (alarm == nullptr || !alarm->enabled()) { + return; + } + + if (alarm->time().hour != local_time->tm_hour || alarm->time().minute != local_time->tm_min) { + return; + } + + if (alarm->_last_triggered_date_key == date_key) { + return; + } + + alarm->_last_triggered_date_key = date_key; + AlarmTriggeredEvent event; + event.alarmId = alarm_id; + event.time = alarm->time(); + mclog::tagInfo(_tag, "alarm triggered, id: {}, time: {}", alarm_id, format_time(event.time)); + _on_triggered.emit(event); + }); +} + +int AlarmClock::addAlarm(const Time24& time, bool enabled) +{ + if (!time.isValid() || _alarms.activeCount() >= AlarmStorageSnapshot::maxAlarmCount) { + mclog::tagError(_tag, "add alarm failed, time: {}, enabled: {}, active count: {}", format_time(time), enabled, + _alarms.activeCount()); + return -1; + } + + auto alarm_id = _alarms.create(std::make_unique(time, enabled)); + mclog::tagInfo(_tag, "add alarm, id: {}, time: {}, enabled: {}", alarm_id, format_time(time), enabled); + if (!persist()) { + mclog::tagError(_tag, "persist after add failed, id: {}", alarm_id); + } + + return alarm_id; +} + +bool AlarmClock::removeAlarm(int alarmId) +{ + if (!_alarms.destroy(alarmId)) { + mclog::tagError(_tag, "remove alarm failed, invalid id: {}", alarmId); + return false; + } + + mclog::tagInfo(_tag, "remove alarm, id: {}", alarmId); + + return persist(); +} + +bool AlarmClock::setAlarmEnabled(int alarmId, bool enabled) +{ + auto* alarm = getAlarm(alarmId); + if (alarm == nullptr) { + mclog::tagError(_tag, "set enabled failed, invalid id: {}, enabled: {}", alarmId, enabled); + return false; + } + + alarm->setEnabled(enabled); + if (!enabled) { + alarm->_last_triggered_date_key = -1; + } + mclog::tagInfo(_tag, "set alarm enabled, id: {}, time: {}, enabled: {}", alarmId, format_time(alarm->time()), + enabled); + return persist(); +} + +bool AlarmClock::setAlarmTime(int alarmId, const Time24& time) +{ + if (!time.isValid()) { + mclog::tagError(_tag, "set time failed, invalid time: {}, id: {}", format_time(time), alarmId); + return false; + } + + auto* alarm = getAlarm(alarmId); + if (alarm == nullptr) { + mclog::tagError(_tag, "set time failed, invalid id: {}, time: {}", alarmId, format_time(time)); + return false; + } + + alarm->setTime(time); + alarm->_last_triggered_date_key = -1; + mclog::tagInfo(_tag, "set alarm time, id: {}, time: {}", alarmId, format_time(time)); + return persist(); +} + +AlarmClock::Alarm* AlarmClock::getAlarm(int alarmId) +{ + return _alarms.get(alarmId); +} + +void AlarmClock::forEachAlarm(const std::function& visitor) +{ + _alarms.forEach([&](Alarm* alarm, int alarm_id) { + if (alarm != nullptr) { + visitor(alarm_id, *alarm); + } + }); +} + +AlarmStorageSnapshot AlarmClock::exportStorage() +{ + AlarmStorageSnapshot snapshot; + snapshot.count = 0; + + _alarms.forEach([&](Alarm* alarm, int) { + if (alarm == nullptr || snapshot.count >= AlarmStorageSnapshot::maxAlarmCount) { + return; + } + + auto& out = snapshot.alarms[snapshot.count++]; + out.hour = alarm->time().hour; + out.minute = alarm->time().minute; + out.enabled = alarm->enabled() ? 1 : 0; + out.reserved = 0; + }); + + return snapshot; +} + +bool AlarmClock::importStorage(const AlarmStorageSnapshot& snapshot, bool persistAfterImport) +{ + _alarms.clear(); + + mclog::tagInfo(_tag, "import storage, count: {}, persist: {}", snapshot.count, persistAfterImport); + + const std::size_t import_count = std::min(snapshot.count, AlarmStorageSnapshot::maxAlarmCount); + for (std::size_t i = 0; i < import_count; ++i) { + const auto& entry = snapshot.alarms[i]; + if (!entry.isValid()) { + mclog::tagError(_tag, "skip invalid storage entry, index: {}, hour: {}, minute: {}, enabled: {}", i, + entry.hour, entry.minute, entry.enabled); + continue; + } + + Time24 time = { + .hour = entry.hour, + .minute = entry.minute, + }; + _alarms.create(std::make_unique(time, entry.enabled != 0)); + } + + if (!persistAfterImport) { + return true; + } + + return persist(); +} + +bool AlarmClock::persist() +{ + auto snapshot = exportStorage(); + bool ok = GetHAL().saveAlarmStorage(snapshot); + if (ok) { + mclog::tagInfo(_tag, "persist storage, count: {}", snapshot.count); + } else { + mclog::tagError(_tag, "persist storage failed, count: {}", snapshot.count); + } + return ok; +} diff --git a/main/apps/app_alarm_clock/model/alarm_clock.h b/main/apps/app_alarm_clock/model/alarm_clock.h new file mode 100644 index 00000000..a94a6582 --- /dev/null +++ b/main/apps/app_alarm_clock/model/alarm_clock.h @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace model { + +class AlarmClock { +public: + struct Time24 { + uint8_t hour = 0; + uint8_t minute = 0; + + bool isValid() const; + }; + + struct AlarmTriggeredEvent { + int alarmId = -1; + Time24 time; + }; + + class Alarm : public uitk::Poolable { + public: + Alarm(const Time24& time, bool enabled); + + const Time24& time() const + { + return _time; + } + + bool enabled() const + { + return _enabled; + } + + void setTime(const Time24& time); + void setEnabled(bool enabled); + + private: + friend class AlarmClock; + + Time24 _time; + bool _enabled = false; + int _last_triggered_date_key = -1; + }; + + bool init(); + void update(); + + int addAlarm(const Time24& time, bool enabled = true); + bool removeAlarm(int alarmId); + bool setAlarmEnabled(int alarmId, bool enabled); + bool setAlarmTime(int alarmId, const Time24& time); + + Alarm* getAlarm(int alarmId); + void forEachAlarm(const std::function& visitor); + + AlarmStorageSnapshot exportStorage(); + bool importStorage(const AlarmStorageSnapshot& snapshot, bool persistAfterImport = false); + + uitk::Signal& onTriggered() + { + return _on_triggered; + } + +private: + bool persist(); + + uitk::ObjectPool _alarms; + uitk::Signal _on_triggered; + uint32_t _last_update_ms = 0; +}; + +} // namespace model diff --git a/main/apps/app_alarm_clock/view/add_alarm.cpp b/main/apps/app_alarm_clock/view/add_alarm.cpp new file mode 100644 index 00000000..fb4eff72 --- /dev/null +++ b/main/apps/app_alarm_clock/view/add_alarm.cpp @@ -0,0 +1,131 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#include "view.h" +#include +#include + +using namespace view; +using namespace uitk::lvgl_cpp; + +namespace { + +constexpr int _panel_width = 466; +constexpr int _panel_height = 466; +constexpr int _selector_width = 160; +constexpr int _selector_height = 200; +constexpr int _ok_width = 374; +constexpr int _ok_height = 130; + +constexpr uint32_t _bg_color = 0x000000; +constexpr uint32_t _label_color = 0xFFFFFF; +constexpr uint32_t _selector_color = 0x343434; +constexpr uint32_t _selector_selected_color = 0x696969; +constexpr uint32_t _ok_color = 0x4AD78C; +constexpr uint32_t _ok_text_color = 0x0F5831; + +std::string build_number_options(int begin, int end) +{ + std::string options; + for (int value = begin; value <= end; ++value) { + char buffer[4] = {}; + std::snprintf(buffer, sizeof(buffer), "%02d", value); + if (!options.empty()) { + options.push_back('\n'); + } + options += buffer; + } + return options; +} + +} // namespace + +void AlarmAddView::init(lv_obj_t* parent) +{ + _panel = std::make_unique(parent); + _panel->align(LV_ALIGN_CENTER, 0, 0); + _panel->setSize(_panel_width, _panel_height); + _panel->setRadius(0); + _panel->setBorderWidth(0); + _panel->setPaddingAll(0); + _panel->setBgColor(lv_color_hex(_bg_color)); + _panel->setBgOpa(LV_OPA_COVER); + _panel->removeFlag(LV_OBJ_FLAG_SCROLLABLE); + + _title_label = std::make_unique