From b52fb62764b2c0e5bf19436f6d6ea05a8012a8eb Mon Sep 17 00:00:00 2001 From: Shadowtrance Date: Fri, 25 Sep 2026 09:51:37 +1000 Subject: [PATCH 1/6] stuff and things Added GC0308 camera driver and wired to CoreS3/Stackchan Changed Stackchan touch to the more reliable ft5x06 driver Added rotation offset to sc2356 camera driver Added optional properties to IMU drivers for swap-xy, swap-xz, swap-yz, invert-x, invert-y, invert-z for accel. fix(m5stack-tab5): hold LCD_RST with a weak pull-up on PI4IO P4 ST7121/ST7123 LCD_RST is a 1.8V pin, so a 3.3V push-pull drive can overvoltage the panel IO. Configure PI4IO-1 P4 as input with a weak pull-up instead. ILI9881C RST accepts 1.65V-3.3V, and all three panels were verified to initialize and run with this change. https://github.com/espressif/esp-bsp/pull/827 Added a few extra shared font icons. Updated esp_audio_codec to 2.5.0, beyond this version has breaking changes for older P4 chip revisions an will not work (yet). App icons are back! Wired in app icons for known apps in the applist and settings. external/unknown apps fallback to LVGL_ICON_SHARED_DEPLOYED_CODE whic AppPackageList also has for consistency. and stuff... --- Devices/m5stack-cores3/device.properties | 7 + Devices/m5stack-cores3/devicetree.yaml | 21 +- Devices/m5stack-cores3/m5stack,cores3.dts | 22 + Devices/m5stack-stackchan/device.properties | 7 + Devices/m5stack-stackchan/devicetree.yaml | 3 +- .../m5stack-stackchan/m5stack,stackchan.dts | 31 +- .../Source/devices/display_detect.cpp | 2 +- Devices/m5stack-tab5/m5stack,tab5.dts | 1 + .../bmi270-module/bindings/bosch,bmi270.yaml | 28 +- .../bmi270-module/include/drivers/bmi270.h | 6 + Drivers/bmi270-module/source/bmi270.cpp | 20 +- Drivers/gc0308-module/CMakeLists.txt | 11 + Drivers/gc0308-module/LICENSE-Apache-2.0.md | 195 +++++++ Drivers/gc0308-module/README.md | 8 + .../bindings/galaxycore,gc0308.yaml | 45 ++ Drivers/gc0308-module/devicetree.yaml | 3 + .../gc0308-module/include/bindings/gc0308.h | 15 + .../gc0308-module/include/drivers/gc0308.h | 124 ++++ Drivers/gc0308-module/include/gc0308_module.h | 14 + Drivers/gc0308-module/source/gc0308.cpp | 548 ++++++++++++++++++ Drivers/gc0308-module/source/module.cpp | 23 + .../bindings/invensense,mpu6886.yaml | 28 +- .../mpu6886-module/include/drivers/mpu6886.h | 6 + Drivers/mpu6886-module/source/mpu6886.cpp | 20 +- .../qmi8658-module/bindings/qst,qmi8658.yaml | 26 + .../qmi8658-module/include/drivers/qmi8658.h | 6 + Drivers/qmi8658-module/source/qmi8658.cpp | 20 +- .../bindings/smartsens,sc2356.yaml | 10 +- .../sc2356-module/include/drivers/sc2356.h | 3 + Drivers/sc2356-module/source/sc2356.cpp | 11 +- Drivers/st7123-module/README.md | 3 + Modules/lvgl-module/assets/generate-all.py | 3 + .../lvgl-module/include/lvgl/icons/shared.h | 3 + .../source-fonts/material_symbols_shared_12.c | 90 +-- .../source-fonts/material_symbols_shared_16.c | 107 ++-- .../source-fonts/material_symbols_shared_20.c | 118 ++-- .../source-fonts/material_symbols_shared_24.c | 123 ++-- .../source-fonts/material_symbols_shared_32.c | 154 +++-- Tactility/Source/Tactility.cpp | 5 + Tactility/Source/app/applist/AppList.cpp | 26 +- .../app/apppackagelist/AppPackageList.cpp | 2 +- Tactility/Source/app/camera/Camera.cpp | 493 ++++++++++++++++ Tactility/Source/app/settings/Settings.cpp | 34 +- .../Source/service/statusbar/Statusbar.cpp | 8 +- Tactility/Source/settings/DisplaySettings.cpp | 10 +- Tactility/idf_component.yml | 6 +- 46 files changed, 2202 insertions(+), 247 deletions(-) create mode 100644 Drivers/gc0308-module/CMakeLists.txt create mode 100644 Drivers/gc0308-module/LICENSE-Apache-2.0.md create mode 100644 Drivers/gc0308-module/README.md create mode 100644 Drivers/gc0308-module/bindings/galaxycore,gc0308.yaml create mode 100644 Drivers/gc0308-module/devicetree.yaml create mode 100644 Drivers/gc0308-module/include/bindings/gc0308.h create mode 100644 Drivers/gc0308-module/include/drivers/gc0308.h create mode 100644 Drivers/gc0308-module/include/gc0308_module.h create mode 100644 Drivers/gc0308-module/source/gc0308.cpp create mode 100644 Drivers/gc0308-module/source/module.cpp create mode 100644 Tactility/Source/app/camera/Camera.cpp diff --git a/Devices/m5stack-cores3/device.properties b/Devices/m5stack-cores3/device.properties index d20fc67dd..65a648bcc 100644 --- a/Devices/m5stack-cores3/device.properties +++ b/Devices/m5stack-cores3/device.properties @@ -19,3 +19,10 @@ display.shape=rectangle display.dpi=200 lvgl.colorDepth=16 + +sdkconfig.CONFIG_ESP_VIDEO_ENABLE_DVP_VIDEO_DEVICE=y +sdkconfig.CONFIG_CAMERA_GC0308=y +sdkconfig.CONFIG_CAMERA_GC0308_DVP_RGB565_BE_320X240_20FPS=y +sdkconfig.CONFIG_CAMERA_GC0308_DVP_RGB565_BE_640X480_16FPS=n +sdkconfig.CONFIG_CAMERA_GC0308_DVP_YUV422_YUYV_640X480_16FPS=n +sdkconfig.CONFIG_CAMERA_GC0308_DVP_DEFAULT_FMT_RGB565_BE_320X240_20FPS=y diff --git a/Devices/m5stack-cores3/devicetree.yaml b/Devices/m5stack-cores3/devicetree.yaml index 348e3507e..37f471cdd 100644 --- a/Devices/m5stack-cores3/devicetree.yaml +++ b/Devices/m5stack-cores3/devicetree.yaml @@ -1,12 +1,13 @@ dependencies: -- Platforms/platform-esp32 -- Drivers/audio-stream-module -- Drivers/aw88298-module -- Drivers/aw9523b-module -- Drivers/axp2101-module -- Drivers/bm8563-module -- Drivers/bmi270-module -- Drivers/es7210-module -- Drivers/ft5x06-module -- Drivers/ili9341-module + - Platforms/platform-esp32 + - Drivers/audio-stream-module + - Drivers/aw88298-module + - Drivers/aw9523b-module + - Drivers/axp2101-module + - Drivers/bm8563-module + - Drivers/gc0308-module + - Drivers/bmi270-module + - Drivers/es7210-module + - Drivers/ft5x06-module + - Drivers/ili9341-module dts: m5stack,cores3.dts diff --git a/Devices/m5stack-cores3/m5stack,cores3.dts b/Devices/m5stack-cores3/m5stack,cores3.dts index aaefa217c..fc3bbb053 100644 --- a/Devices/m5stack-cores3/m5stack,cores3.dts +++ b/Devices/m5stack-cores3/m5stack,cores3.dts @@ -20,6 +20,7 @@ #include #include #include +#include // SD card is not supported because of a hardware problem: // The display uses pin 35 as input (MISO), but the SPI bus (and SD card) uses the same pin as DC (output). @@ -64,6 +65,7 @@ bmi270 { compatible = "bosch,bmi270"; reg = <0x69>; + invert-x; }; bm8563 { @@ -151,6 +153,26 @@ // capsules on their own. input-gain-percent = <400>; }; + + gc0308 { + compatible = "galaxycore,gc0308"; + reg = <0x21>; + rotation-offset = <0>; + xclk-frequency-hz = <20000000>; + pin-d0 = <&gpio0 39 GPIO_FLAG_NONE>; + pin-d1 = <&gpio0 40 GPIO_FLAG_NONE>; + pin-d2 = <&gpio0 41 GPIO_FLAG_NONE>; + pin-d3 = <&gpio0 42 GPIO_FLAG_NONE>; + pin-d4 = <&gpio0 15 GPIO_FLAG_NONE>; + pin-d5 = <&gpio0 16 GPIO_FLAG_NONE>; + pin-d6 = <&gpio0 48 GPIO_FLAG_NONE>; + pin-d7 = <&gpio0 47 GPIO_FLAG_NONE>; + pin-vsync = <&gpio0 46 GPIO_FLAG_NONE>; + pin-de = <&gpio0 38 GPIO_FLAG_NONE>; + pin-pclk = <&gpio0 45 GPIO_FLAG_NONE>; + pin-xclk = <&gpio0 2 GPIO_FLAG_NONE>; + pin-reset = <&aw9523b 8 GPIO_FLAG_ACTIVE_LOW>; + }; }; port_a: grove0 { diff --git a/Devices/m5stack-stackchan/device.properties b/Devices/m5stack-stackchan/device.properties index e145ac025..cbe96d54f 100644 --- a/Devices/m5stack-stackchan/device.properties +++ b/Devices/m5stack-stackchan/device.properties @@ -19,3 +19,10 @@ display.shape=rectangle display.dpi=200 lvgl.colorDepth=16 + +sdkconfig.CONFIG_ESP_VIDEO_ENABLE_DVP_VIDEO_DEVICE=y +sdkconfig.CONFIG_CAMERA_GC0308=y +sdkconfig.CONFIG_CAMERA_GC0308_DVP_RGB565_BE_320X240_20FPS=y +sdkconfig.CONFIG_CAMERA_GC0308_DVP_RGB565_BE_640X480_16FPS=n +sdkconfig.CONFIG_CAMERA_GC0308_DVP_YUV422_YUYV_640X480_16FPS=n +sdkconfig.CONFIG_CAMERA_GC0308_DVP_DEFAULT_FMT_RGB565_BE_320X240_20FPS=y diff --git a/Devices/m5stack-stackchan/devicetree.yaml b/Devices/m5stack-stackchan/devicetree.yaml index 4e30a838d..c7bb0441c 100644 --- a/Devices/m5stack-stackchan/devicetree.yaml +++ b/Devices/m5stack-stackchan/devicetree.yaml @@ -5,9 +5,10 @@ dependencies: - Drivers/aw9523b-module - Drivers/axp2101-module - Drivers/bm8563-module + - Drivers/gc0308-module - Drivers/bmi270-module - Drivers/es7210-module - - Drivers/ft6x36-module + - Drivers/ft5x06-module - Drivers/ili9341-module - Drivers/ina226-module - Drivers/py32ioexpander-module diff --git a/Devices/m5stack-stackchan/m5stack,stackchan.dts b/Devices/m5stack-stackchan/m5stack,stackchan.dts index 706f2dfb0..c7416fafd 100644 --- a/Devices/m5stack-stackchan/m5stack,stackchan.dts +++ b/Devices/m5stack-stackchan/m5stack,stackchan.dts @@ -19,10 +19,11 @@ #include #include #include -#include +#include #include #include #include +#include // Reference: https://docs.m5stack.com/en/StackChan / { @@ -69,6 +70,7 @@ bmi270 { compatible = "bosch,bmi270"; reg = <0x69>; + invert-x; }; bm8563 { @@ -143,10 +145,10 @@ }; touch { - compatible = "focaltech,ft6x36"; + compatible = "focaltech,ft5x06"; reg = <0x38>; - x-max = <319>; - y-max = <239>; + x-max = <320>; + y-max = <240>; pin-reset = <&aw9523b 0 GPIO_FLAG_NONE>; }; @@ -165,8 +167,27 @@ input-gain-percent = <400>; }; + gc0308 { + compatible = "galaxycore,gc0308"; + reg = <0x21>; + rotation-offset = <0>; + xclk-frequency-hz = <20000000>; + pin-d0 = <&gpio0 39 GPIO_FLAG_NONE>; + pin-d1 = <&gpio0 40 GPIO_FLAG_NONE>; + pin-d2 = <&gpio0 41 GPIO_FLAG_NONE>; + pin-d3 = <&gpio0 42 GPIO_FLAG_NONE>; + pin-d4 = <&gpio0 15 GPIO_FLAG_NONE>; + pin-d5 = <&gpio0 16 GPIO_FLAG_NONE>; + pin-d6 = <&gpio0 48 GPIO_FLAG_NONE>; + pin-d7 = <&gpio0 47 GPIO_FLAG_NONE>; + pin-vsync = <&gpio0 46 GPIO_FLAG_NONE>; + pin-de = <&gpio0 38 GPIO_FLAG_NONE>; + pin-pclk = <&gpio0 45 GPIO_FLAG_NONE>; + pin-xclk = <&gpio0 2 GPIO_FLAG_NONE>; + pin-reset = <&aw9523b 8 GPIO_FLAG_ACTIVE_LOW>; + }; + // TODO: Si12T 3-zone head touch @ 0x68 — INT active-low, 10kΩ pull-up to 3.3V; driver not yet implemented - // TODO: GC0308 camera @ 0x21 — requires i2c_master driver, not yet available // TODO: LTR-553ALS-WA proximity/light @ 0x23 — no driver yet // TODO: BMM150 magnetometer @ 0x10 — accessible only via BMI270 aux I2C }; diff --git a/Devices/m5stack-tab5/Source/devices/display_detect.cpp b/Devices/m5stack-tab5/Source/devices/display_detect.cpp index 2e88f9fcf..d08167c85 100644 --- a/Devices/m5stack-tab5/Source/devices/display_detect.cpp +++ b/Devices/m5stack-tab5/Source/devices/display_detect.cpp @@ -56,7 +56,7 @@ bool pulse_display_reset_pins(Device* io_expander0) { gpio_descriptor_set_level(lcd_reset_pin, true); gpio_descriptor_set_level(touch_reset_pin, true); vTaskDelay(pdMS_TO_TICKS(10)); - gpio_descriptor_set_level(lcd_reset_pin, false); + gpio_descriptor_set_flags(lcd_reset_pin, GPIO_FLAG_DIRECTION_INPUT | GPIO_FLAG_PULL_UP); gpio_descriptor_set_level(touch_reset_pin, false); gpio_descriptor_release(lcd_reset_pin); diff --git a/Devices/m5stack-tab5/m5stack,tab5.dts b/Devices/m5stack-tab5/m5stack,tab5.dts index 615a5cc81..59452d951 100644 --- a/Devices/m5stack-tab5/m5stack,tab5.dts +++ b/Devices/m5stack-tab5/m5stack,tab5.dts @@ -122,6 +122,7 @@ sc2356 { compatible = "smartsens,sc2356"; + rotation-offset = <270>; reg = <0x36>; // Pulsed by the sc2356 driver itself in start_device, not a gpio-hog: the driver's // I2C probe otherwise races a bare hog's pin release with no settle delay (the hog diff --git a/Drivers/bmi270-module/bindings/bosch,bmi270.yaml b/Drivers/bmi270-module/bindings/bosch,bmi270.yaml index 188955bb2..f35b7d19d 100644 --- a/Drivers/bmi270-module/bindings/bosch,bmi270.yaml +++ b/Drivers/bmi270-module/bindings/bosch,bmi270.yaml @@ -1,5 +1,31 @@ description: Bosch BMI270 6-axis IMU -include: ["i2c-device.yaml"] +include: [ "i2c-device.yaml" ] compatible: "bosch,bmi270" + +properties: + swap-xy: + type: boolean + default: false + description: Swap the X and Y axes + swap-xz: + type: boolean + default: false + description: Swap the X and Z axes + swap-yz: + type: boolean + default: false + description: Swap the Y and Z axes + invert-x: + type: boolean + default: false + description: Invert the X axis + invert-y: + type: boolean + default: false + description: Invert the Y axis + invert-z: + type: boolean + default: false + description: Invert the Z axis diff --git a/Drivers/bmi270-module/include/drivers/bmi270.h b/Drivers/bmi270-module/include/drivers/bmi270.h index ece8ef288..3f91b3bc3 100644 --- a/Drivers/bmi270-module/include/drivers/bmi270.h +++ b/Drivers/bmi270-module/include/drivers/bmi270.h @@ -14,6 +14,12 @@ extern "C" { struct Bmi270Config { /** Address on bus */ uint8_t address; + bool swap_xy; + bool swap_xz; + bool swap_yz; + bool invert_x; + bool invert_y; + bool invert_z; }; /** diff --git a/Drivers/bmi270-module/source/bmi270.cpp b/Drivers/bmi270-module/source/bmi270.cpp index c42ba8da4..b3b52b71a 100644 --- a/Drivers/bmi270-module/source/bmi270.cpp +++ b/Drivers/bmi270-module/source/bmi270.cpp @@ -6,6 +6,8 @@ #include #include +#include + #define TAG "BMI270" static constexpr uint8_t REG_CHIP_ID = 0x00; // read: expect 0x24 @@ -165,9 +167,21 @@ error_t bmi270_read_accel(Device* device, ImuAccelData* data) { return static_cast(static_cast(hi) << 8 | lo); }; - data->ax = toI16(buffer[0], buffer[1]) * ACCEL_SCALE; - data->ay = toI16(buffer[2], buffer[3]) * ACCEL_SCALE; - data->az = toI16(buffer[4], buffer[5]) * ACCEL_SCALE; + float axes[3] = { + toI16(buffer[0], buffer[1]) * ACCEL_SCALE, + toI16(buffer[2], buffer[3]) * ACCEL_SCALE, + toI16(buffer[4], buffer[5]) * ACCEL_SCALE, + }; + const auto& config = *GET_CONFIG(device); + if (config.swap_xy) std::swap(axes[0], axes[1]); + if (config.swap_xz) std::swap(axes[0], axes[2]); + if (config.swap_yz) std::swap(axes[1], axes[2]); + if (config.invert_x) axes[0] = -axes[0]; + if (config.invert_y) axes[1] = -axes[1]; + if (config.invert_z) axes[2] = -axes[2]; + data->ax = axes[0]; + data->ay = axes[1]; + data->az = axes[2]; return ERROR_NONE; } diff --git a/Drivers/gc0308-module/CMakeLists.txt b/Drivers/gc0308-module/CMakeLists.txt new file mode 100644 index 000000000..7bbf21583 --- /dev/null +++ b/Drivers/gc0308-module/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.20) + +include("${CMAKE_CURRENT_LIST_DIR}/../../Buildscripts/module.cmake") + +file(GLOB_RECURSE SOURCE_FILES "source/*.c*") + +tactility_add_module(gc0308-module + SRCS ${SOURCE_FILES} + INCLUDE_DIRS include/ + REQUIRES TactilityKernel platform-esp32 esp_video esp_cam_sensor driver esp_new_jpeg +) diff --git a/Drivers/gc0308-module/LICENSE-Apache-2.0.md b/Drivers/gc0308-module/LICENSE-Apache-2.0.md new file mode 100644 index 000000000..f5f4b8b5e --- /dev/null +++ b/Drivers/gc0308-module/LICENSE-Apache-2.0.md @@ -0,0 +1,195 @@ +Apache License +============== + +_Version 2.0, January 2004_ +_<>_ + +### Terms and Conditions for use, reproduction, and distribution + +#### 1. Definitions + +“License” shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +“Licensor” shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +“Legal Entity” shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, “control” means **(i)** the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the +outstanding shares, or **(iii)** beneficial ownership of such entity. + +“You” (or “Your”) shall mean an individual or Legal Entity exercising +permissions granted by this License. + +“Source” form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +“Object” form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +“Work” shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +“Derivative Works” shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +“Contribution” shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +“submitted” means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as “Not a Contribution.” + +“Contributor” shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +#### 2. Grant of Copyright License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +#### 3. Grant of Patent License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +#### 4. Redistribution + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +* **(a)** You must give any other recipients of the Work or Derivative Works a copy of +this License; and +* **(b)** You must cause any modified files to carry prominent notices stating that You +changed the files; and +* **(c)** You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +#### 5. Submission of Contributions + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +#### 6. Trademarks + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +#### 7. Disclaimer of Warranty + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +#### 8. Limitation of Liability + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +#### 9. Accepting Warranty or Additional Liability + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +_END OF TERMS AND CONDITIONS_ + +### APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets `[]` replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same “printed page” as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/Drivers/gc0308-module/README.md b/Drivers/gc0308-module/README.md new file mode 100644 index 000000000..ff7ee3f09 --- /dev/null +++ b/Drivers/gc0308-module/README.md @@ -0,0 +1,8 @@ +# GC0308 Camera Driver + +A driver for the `GC0308` 0.3MP camera sensor by GalaxyCore. + +- Datasheet: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/core/K128%20CoreS3/GC0308.PDF +- ESP Video Components: https://github.com/espressif/esp-video-components + +License: [Apache v2.0](LICENSE-Apache-2.0.md) diff --git a/Drivers/gc0308-module/bindings/galaxycore,gc0308.yaml b/Drivers/gc0308-module/bindings/galaxycore,gc0308.yaml new file mode 100644 index 000000000..98f7633ce --- /dev/null +++ b/Drivers/gc0308-module/bindings/galaxycore,gc0308.yaml @@ -0,0 +1,45 @@ +description: GalaxyCore GC0308 0.3MP camera sensor + +include: [ "i2c-device.yaml" ] + +compatible: "galaxycore,gc0308" + +properties: + rotation-offset: + type: int + enum: [ 0, 90, 180, 270 ] + default: 0 + description: Clockwise mounting correction in degrees (0, 90, 180 or 270). + xclk-frequency-hz: + type: int + default: 20000000 + description: DVP sensor master clock frequency. + pin-d0: + type: phandles + pin-d1: + type: phandles + pin-d2: + type: phandles + pin-d3: + type: phandles + pin-d4: + type: phandles + pin-d5: + type: phandles + pin-d6: + type: phandles + pin-d7: + type: phandles + pin-vsync: + type: phandles + pin-de: + type: phandles + pin-pclk: + type: phandles + pin-xclk: + type: phandles + pin-reset: + type: phandles + default: GPIO_PIN_SPEC_NONE + description: > + Reset GPIO pin. Pulsed (asserted then released, with a settle delay) before the sensor is probed in start_device. Typically wired through a board's IO expander rather than a direct SoC GPIO, so it's handled here via the generic gpio_descriptor API. diff --git a/Drivers/gc0308-module/devicetree.yaml b/Drivers/gc0308-module/devicetree.yaml new file mode 100644 index 000000000..9424b9a3a --- /dev/null +++ b/Drivers/gc0308-module/devicetree.yaml @@ -0,0 +1,3 @@ +dependencies: +- TactilityKernel +bindings: bindings diff --git a/Drivers/gc0308-module/include/bindings/gc0308.h b/Drivers/gc0308-module/include/bindings/gc0308.h new file mode 100644 index 000000000..80bf74df7 --- /dev/null +++ b/Drivers/gc0308-module/include/bindings/gc0308.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +DEFINE_DEVICETREE(gc0308, struct Gc0308Config) + +#ifdef __cplusplus +} +#endif diff --git a/Drivers/gc0308-module/include/drivers/gc0308.h b/Drivers/gc0308-module/include/drivers/gc0308.h new file mode 100644 index 000000000..982a64aa7 --- /dev/null +++ b/Drivers/gc0308-module/include/drivers/gc0308.h @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include +#include +#include +#include +#include + +struct Device; + +#ifdef __cplusplus +extern "C" { +#endif + +struct Gc0308Config { + /** SCCB I2C address (0x21) */ + uint8_t address; + + /** Clockwise mounting correction applied after the requested display rotation. */ + uint16_t rotation_offset; + + uint32_t xclk_frequency_hz; + struct GpioPinSpec pin_d0; + struct GpioPinSpec pin_d1; + struct GpioPinSpec pin_d2; + struct GpioPinSpec pin_d3; + struct GpioPinSpec pin_d4; + struct GpioPinSpec pin_d5; + struct GpioPinSpec pin_d6; + struct GpioPinSpec pin_d7; + struct GpioPinSpec pin_vsync; + struct GpioPinSpec pin_de; + struct GpioPinSpec pin_pclk; + struct GpioPinSpec pin_xclk; + + // Reset pin. GPIO_PIN_SPEC_NONE if the sensor's reset is tied high on the board (or otherwise + // not under our control), in which case start_device skips the reset pulse entirely and goes + // straight to probing. + struct GpioPinSpec pin_reset; +}; + +/** + * Opaque camera handle returned by gc0308_open(). + * Not thread-safe: the capture flow (gc0308_get_frame/gc0308_capture_jpeg + gc0308_release_frame) + * must be driven from a single consumer at a time. + */ +typedef CameraHandle Gc0308Handle; + +/** + * Initialize the esp_video DVP subsystem and open the DVP video device. + * Reuses the parent I2C controller's bus handle for SCCB communication. + * Must be called before any other gc0308_* functions. + * @param device the gc0308 device from the device tree + * @param handle out: opaque handle to pass to subsequent calls + * @return ERROR_NONE on success + */ +error_t gc0308_open(struct Device* device, Gc0308Handle* handle); + +/** + * Stop streaming, unmap frame buffers, and release all resources. + * @param handle handle returned by gc0308_open() + * @return ERROR_NONE on success + */ +error_t gc0308_close(Gc0308Handle handle); + +/** + * Dequeue one RGB565 frame. Blocks until a frame is available or the timeout expires. + * The caller must call gc0308_release_frame() to return the buffer to the camera queue. + * @param handle handle returned by gc0308_open() + * @param buf out: pointer to the DMA-mapped RGB565 frame buffer + * @param len out: byte length of buf (width * height * 2) + * @param timeout_ms maximum time to wait in milliseconds + * @param out_width optional out: width of buf at the moment it was produced, atomic with the frame data (may be NULL) + * @param out_height optional out: height of buf at the moment it was produced, atomic with the frame data (may be NULL) + * @return ERROR_NONE on success, ERROR_TIMEOUT if no frame arrived in time + */ +error_t gc0308_get_frame(Gc0308Handle handle, uint8_t** buf, size_t* len, uint32_t timeout_ms, uint32_t* out_width, uint32_t* out_height); + +/** + * Return the last dequeued frame buffer to the V4L2 capture queue. + * Must be called after each successful gc0308_get_frame(). + * @param handle handle returned by gc0308_open() + * @return ERROR_NONE on success + */ +error_t gc0308_release_frame(Gc0308Handle handle); + +/** + * Return the frame width in pixels. + * @param handle handle returned by gc0308_open() + */ +uint32_t gc0308_get_width(Gc0308Handle handle); + +/** + * Return the frame height in pixels. + * @param handle handle returned by gc0308_open() + */ +uint32_t gc0308_get_height(Gc0308Handle handle); + +/** + * Set the requested clockwise rotation. Subsequent frames are software-rotated + * and their reported dimensions reflect the requested orientation. + * Can be called at any time while the handle is open, including during streaming. + * @param handle handle returned by gc0308_open() + * @param rotation new rotation + * @return ERROR_NONE on success + */ +error_t gc0308_set_rotation(Gc0308Handle handle, CameraRotation rotation); + +/** + * Capture one frame and JPEG-encode it using the hardware JPEG encoder. + * Allocates output buffer in SPIRAM; caller must free it with heap_caps_free(). + * @param handle handle returned by gc0308_open() + * @param out_buf out: pointer to JPEG-encoded data (caller must free) + * @param out_len out: byte length of JPEG data + * @param quality JPEG quality 1-100 + * @return ERROR_NONE on success + */ +error_t gc0308_capture_jpeg(Gc0308Handle handle, uint8_t** out_buf, size_t* out_len, uint8_t quality); + +#ifdef __cplusplus +} +#endif diff --git a/Drivers/gc0308-module/include/gc0308_module.h b/Drivers/gc0308-module/include/gc0308_module.h new file mode 100644 index 000000000..6ba0916f1 --- /dev/null +++ b/Drivers/gc0308-module/include/gc0308_module.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct Module gc0308_module; + +#ifdef __cplusplus +} +#endif diff --git a/Drivers/gc0308-module/source/gc0308.cpp b/Drivers/gc0308-module/source/gc0308.cpp new file mode 100644 index 000000000..c74e1cb73 --- /dev/null +++ b/Drivers/gc0308-module/source/gc0308.cpp @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: Apache-2.0 +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define TAG "GC0308" + +static constexpr int VIDEO_BUFFER_COUNT = 2; +static bool s_video_initialized = false; +static SemaphoreHandle_t s_video_init_mutex = nullptr; +static uint32_t s_video_handle_count = 0; + +struct Gc0308State : CameraHandleData { + int fd; + // Native sensor output dimensions + uint32_t native_width; + uint32_t native_height; + // Frames are returned in the requested orientation. + uint32_t width; + uint32_t height; + uint8_t* buffers[VIDEO_BUFFER_COUNT]; + size_t buf_lengths[VIDEO_BUFFER_COUNT]; + int last_dqbuf_index; + uint8_t* frame_buf; + size_t frame_buf_size; + jpeg_enc_handle_t encoder; + i2c_master_bus_handle_t sccb_bus; + CameraRotation rotation; + SemaphoreHandle_t rotation_mutex; // protects rotation, width, height + uint32_t encoder_width; + uint32_t encoder_height; +}; + +#define GET_CONFIG(device) (static_cast((device)->config)) + +static CameraRotation add_rotation(CameraRotation rotation, uint16_t offset) { + return static_cast((static_cast(rotation) + offset) % 360); +} + +static error_t reset_pulse(GpioDescriptor* descriptor) { + // Release is attempted unconditionally, even if assert failed - leaving the expander output + // asserted on an assert failure would hold the sensor in reset for the rest of boot. + const bool assert_ok = gpio_descriptor_set_level(descriptor, true) == ERROR_NONE; // assert + vTaskDelay(pdMS_TO_TICKS(10)); + const bool release_ok = gpio_descriptor_set_level(descriptor, false) == ERROR_NONE; // release + vTaskDelay(pdMS_TO_TICKS(10)); + return (assert_ok && release_ok) ? ERROR_NONE : ERROR_RESOURCE; +} + +static error_t start(Device* device) { + auto* i2c = device_get_parent(device); + if (device_get_type(i2c) != &I2C_CONTROLLER_TYPE) { + LOG_E(TAG, "Parent is not an I2C controller"); + return ERROR_RESOURCE; + } + + const auto* config = GET_CONFIG(device); + //auto address = config->address; + + // Reset is pulsed (not just held released) so the sensor reaches a known state regardless of + // whatever it inherited from a previous boot + if (config->pin_reset.gpio_controller != nullptr) { + // Merge with the devicetree-supplied flags (e.g. GPIO_FLAG_ACTIVE_LOW) rather than + // overwriting them, so a board that wires reset active-high isn't silently forced to + // active-low polarity. + auto* reset_descriptor = gpio_descriptor_acquire(config->pin_reset.gpio_controller, config->pin_reset.pin, config->pin_reset.flags | GPIO_FLAG_DIRECTION_OUTPUT, GPIO_OWNER_GPIO); + if (reset_descriptor == nullptr) { + LOG_E(TAG, "Failed to acquire reset pin"); + return ERROR_RESOURCE; + } + error_t reset_error = reset_pulse(reset_descriptor); + gpio_descriptor_release(reset_descriptor); + if (reset_error != ERROR_NONE) { + LOG_E(TAG, "Failed to pulse reset pin"); + return reset_error; + } + } + + return ERROR_NONE; +} + +static error_t stop([[maybe_unused]] Device* device) { + return ERROR_NONE; +} + +static SemaphoreHandle_t get_video_init_mutex() { + static portMUX_TYPE creation_lock = portMUX_INITIALIZER_UNLOCKED; + portENTER_CRITICAL(&creation_lock); + if (!s_video_init_mutex) { + s_video_init_mutex = xSemaphoreCreateMutex(); + if (!s_video_init_mutex) { + LOG_E(TAG, "Failed to create video init mutex"); + } + } + portEXIT_CRITICAL(&creation_lock); + return s_video_init_mutex; +} + +extern "C" { + +error_t gc0308_open(Device* device, Gc0308Handle* out_handle) { + if (!device || !out_handle) return ERROR_INVALID_ARGUMENT; + + auto* state = static_cast(heap_caps_calloc(1, sizeof(Gc0308State), MALLOC_CAP_DEFAULT)); + if (!state) return ERROR_OUT_OF_MEMORY; + state->device = device; + state->fd = -1; + state->last_dqbuf_index = -1; + state->rotation_mutex = xSemaphoreCreateMutex(); + if (!state->rotation_mutex) { + heap_caps_free(state); + return ERROR_OUT_OF_MEMORY; + } + + state->rotation = CAMERA_ROTATION_0; + struct v4l2_format fmt = {}; + + // Retrieve bus handle from the parent I2C controller + auto* i2c = device_get_parent(device); + state->sccb_bus = esp32_i2c_master_get_bus_handle(i2c); + if (!state->sccb_bus) { + LOG_E(TAG, "Failed to get i2c_master bus handle from parent device"); + vSemaphoreDelete(state->rotation_mutex); + heap_caps_free(state); + return ERROR_RESOURCE; + } + + const auto* config = GET_CONFIG(device); + bool video_handle_acquired = false; + + // esp_video_init only needs to run once across all gc0308_open calls. + { + SemaphoreHandle_t init_mutex = get_video_init_mutex(); + if (!init_mutex) { + vSemaphoreDelete(state->rotation_mutex); + heap_caps_free(state); + return ERROR_OUT_OF_MEMORY; + } + xSemaphoreTake(init_mutex, portMAX_DELAY); + if (!s_video_initialized) { + esp_video_init_dvp_config_t dvp_config = { + .sccb_config = { + .init_sccb = false, + .i2c_handle = state->sccb_bus, + .freq = 100000, + }, + .reset_pin = GPIO_NUM_NC, + .pwdn_pin = GPIO_NUM_NC, + .dvp_pin = { + .data_width = CAM_CTLR_DATA_WIDTH_8, + .data_io = { + static_cast(config->pin_d0.pin), + static_cast(config->pin_d1.pin), + static_cast(config->pin_d2.pin), + static_cast(config->pin_d3.pin), + static_cast(config->pin_d4.pin), + static_cast(config->pin_d5.pin), + static_cast(config->pin_d6.pin), + static_cast(config->pin_d7.pin), + }, + .vsync_io = static_cast(config->pin_vsync.pin), + .de_io = static_cast(config->pin_de.pin), + .pclk_io = static_cast(config->pin_pclk.pin), + .xclk_io = static_cast(config->pin_xclk.pin), + }, + .xclk_freq = config->xclk_frequency_hz, + }; + esp_video_init_config_t video_config = { .dvp = &dvp_config }; + esp_err_t esp_err = esp_video_init_with_flags(&video_config, ESP_VIDEO_INIT_FLAGS_DVP); + if (esp_err != ESP_OK) { + LOG_E(TAG, "esp_video_init DVP failed: %s", esp_err_to_name(esp_err)); + xSemaphoreGive(init_mutex); + vSemaphoreDelete(state->rotation_mutex); + heap_caps_free(state); + return ERROR_RESOURCE; + } + s_video_initialized = true; + } + ++s_video_handle_count; + video_handle_acquired = true; + xSemaphoreGive(init_mutex); + } + + state->fd = open(ESP_VIDEO_DVP_DEVICE_NAME, O_RDONLY); + if (state->fd < 0) { + LOG_E(TAG, "Failed to open %s", ESP_VIDEO_DVP_DEVICE_NAME); + goto err_close; + } + + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(state->fd, VIDIOC_G_FMT, &fmt) != 0) { + LOG_E(TAG, "VIDIOC_G_FMT failed"); + goto err_close; + } + state->native_width = fmt.fmt.pix.width; + state->native_height = fmt.fmt.pix.height; + state->frame_buf_size = (size_t)state->native_width * state->native_height * 2; + state->rotation = add_rotation(CAMERA_ROTATION_0, config->rotation_offset); + if (state->rotation == CAMERA_ROTATION_90 || state->rotation == CAMERA_ROTATION_270) { + state->width = state->native_height; + state->height = state->native_width; + } else { + state->width = state->native_width; + state->height = state->native_height; + } + state->frame_buf = static_cast(heap_caps_aligned_alloc(16, state->frame_buf_size, MALLOC_CAP_SPIRAM)); + if (!state->frame_buf) goto err_close; + + { + struct v4l2_requestbuffers request = {}; + request.count = VIDEO_BUFFER_COUNT; + request.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + request.memory = V4L2_MEMORY_MMAP; + if (ioctl(state->fd, VIDIOC_REQBUFS, &request) != 0) goto err_close; + } + for (int i = 0; i < VIDEO_BUFFER_COUNT; i++) { + struct v4l2_buffer buffer = {}; + buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buffer.memory = V4L2_MEMORY_MMAP; + buffer.index = i; + if (ioctl(state->fd, VIDIOC_QUERYBUF, &buffer) != 0) goto err_unmap; + state->buf_lengths[i] = buffer.length; + state->buffers[i] = static_cast(mmap(nullptr, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, state->fd, buffer.m.offset)); + if (state->buffers[i] == MAP_FAILED) { + state->buffers[i] = nullptr; + goto err_unmap; + } + if (ioctl(state->fd, VIDIOC_QBUF, &buffer) != 0) goto err_unmap; + } + { + int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(state->fd, VIDIOC_STREAMON, &type) != 0) goto err_unmap; + } + + { + jpeg_enc_config_t encoder_config = { + .width = static_cast(state->width), + .height = static_cast(state->height), + .src_type = JPEG_PIXEL_FORMAT_RGB565_LE, + .subsampling = JPEG_SUBSAMPLE_420, + .quality = 80, + .rotate = JPEG_ROTATE_0D, + .task_enable = false, + .hfm_task_priority = 13, + .hfm_task_core = 1, + }; + if (jpeg_enc_open(&encoder_config, &state->encoder) != JPEG_ERR_OK) goto err_unmap; + state->encoder_width = state->width; + state->encoder_height = state->height; + } + + LOG_I(TAG, "Video format: %lux%lu native", (unsigned long)state->native_width, (unsigned long)state->native_height); + + *out_handle = state; + return ERROR_NONE; + +err_unmap: + if (state->encoder) { + jpeg_enc_close(state->encoder); + state->encoder = nullptr; + } + { + int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ioctl(state->fd, VIDIOC_STREAMOFF, &type); + } + for (int i = 0; i < VIDEO_BUFFER_COUNT; i++) { + if (state->buffers[i]) munmap(state->buffers[i], state->buf_lengths[i]); + } +err_close: + if (state->fd >= 0) close(state->fd); + if (video_handle_acquired) { + SemaphoreHandle_t init_mutex = get_video_init_mutex(); + xSemaphoreTake(init_mutex, portMAX_DELAY); + --s_video_handle_count; + if (s_video_handle_count == 0 && s_video_initialized) { + esp_video_deinit_with_flags(ESP_VIDEO_INIT_FLAGS_DVP); + s_video_initialized = false; + } + xSemaphoreGive(init_mutex); + } + vSemaphoreDelete(state->rotation_mutex); + heap_caps_free(state->frame_buf); + heap_caps_free(state); + return ERROR_RESOURCE; +} + +error_t gc0308_close(Gc0308Handle handle) { + if (!handle) return ERROR_INVALID_ARGUMENT; + auto* state = static_cast(handle); + + if (state->encoder) jpeg_enc_close(state->encoder); + int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ioctl(state->fd, VIDIOC_STREAMOFF, &type); + for (int i = 0; i < VIDEO_BUFFER_COUNT; i++) { + if (state->buffers[i]) munmap(state->buffers[i], state->buf_lengths[i]); + } + heap_caps_free(state->frame_buf); + close(state->fd); + SemaphoreHandle_t init_mutex = get_video_init_mutex(); + xSemaphoreTake(init_mutex, portMAX_DELAY); + if (s_video_handle_count > 0) --s_video_handle_count; + if (s_video_handle_count == 0 && s_video_initialized) { + esp_video_deinit_with_flags(ESP_VIDEO_INIT_FLAGS_DVP); + s_video_initialized = false; + } + xSemaphoreGive(init_mutex); + if (state->rotation_mutex) vSemaphoreDelete(state->rotation_mutex); + // Bus handle is owned by esp32_i2c_master driver - do not delete it + heap_caps_free(state); + return ERROR_NONE; +} + +error_t gc0308_set_rotation(Gc0308Handle handle, CameraRotation rotation) { + if (!handle) return ERROR_INVALID_ARGUMENT; + auto* state = static_cast(handle); + CameraRotation effective_rotation = add_rotation(rotation, GET_CONFIG(state->device)->rotation_offset); + + xSemaphoreTake(state->rotation_mutex, portMAX_DELAY); + + if (state->rotation == effective_rotation) { + xSemaphoreGive(state->rotation_mutex); + return ERROR_NONE; + } + + bool needs_swap = (effective_rotation == CAMERA_ROTATION_90 || effective_rotation == CAMERA_ROTATION_270); + + state->rotation = effective_rotation; + if (needs_swap) { + state->width = state->native_height; + state->height = state->native_width; + } else { + state->width = state->native_width; + state->height = state->native_height; + } + + xSemaphoreGive(state->rotation_mutex); + return ERROR_NONE; +} + +error_t gc0308_get_frame(Gc0308Handle handle, uint8_t** buf, size_t* len, uint32_t timeout_ms, uint32_t* out_width, uint32_t* out_height) { + if (!handle || !buf || !len) return ERROR_INVALID_ARGUMENT; + auto* state = static_cast(handle); + + struct pollfd poll_descriptor = { .fd = state->fd, .events = POLLIN }; + int poll_result = poll(&poll_descriptor, 1, static_cast(timeout_ms)); + if (poll_result == 0) return ERROR_TIMEOUT; + if (poll_result < 0) return ERROR_RESOURCE; + + struct v4l2_buffer buffer = {}; + buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buffer.memory = V4L2_MEMORY_MMAP; + if (ioctl(state->fd, VIDIOC_DQBUF, &buffer) != 0) return ERROR_RESOURCE; + state->last_dqbuf_index = static_cast(buffer.index); + + if (buffer.index >= VIDEO_BUFFER_COUNT || buffer.bytesused != state->frame_buf_size || + (buffer.flags & V4L2_BUF_FLAG_ERROR) != 0) { + gc0308_release_frame(handle); + return ERROR_RESOURCE; + } + + xSemaphoreTake(state->rotation_mutex, portMAX_DELAY); + CameraRotation rotation = state->rotation; + uint32_t frame_width = state->width; + uint32_t frame_height = state->height; + + size_t copy_length = state->frame_buf_size; + const uint8_t* source = state->buffers[buffer.index]; + for (uint32_t y = 0; y < frame_height; y++) { + for (uint32_t x = 0; x < frame_width; x++) { + uint32_t source_x; + uint32_t source_y; + switch (rotation) { + case CAMERA_ROTATION_90: + source_x = y; + source_y = state->native_height - 1 - x; + break; + case CAMERA_ROTATION_180: + source_x = state->native_width - 1 - x; + source_y = state->native_height - 1 - y; + break; + case CAMERA_ROTATION_270: + source_x = state->native_width - 1 - y; + source_y = x; + break; + default: + source_x = x; + source_y = y; + break; + } + + size_t source_offset = ((size_t)source_y * state->native_width + source_x) * 2; + size_t destination_offset = ((size_t)y * frame_width + x) * 2; + if (source_offset + 1 >= copy_length) continue; + state->frame_buf[destination_offset] = source[source_offset + 1]; + state->frame_buf[destination_offset + 1] = source[source_offset]; + } + } + xSemaphoreGive(state->rotation_mutex); + + *buf = state->frame_buf; + *len = (size_t)frame_width * frame_height * 2; + if (out_width != nullptr) { + *out_width = frame_width; + } + if (out_height != nullptr) { + *out_height = frame_height; + } + return ERROR_NONE; +} + +error_t gc0308_release_frame(Gc0308Handle handle) { + if (!handle) return ERROR_INVALID_ARGUMENT; + auto* state = static_cast(handle); + + if (state->last_dqbuf_index < 0) return ERROR_INVALID_STATE; + struct v4l2_buffer buffer = {}; + buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buffer.memory = V4L2_MEMORY_MMAP; + buffer.index = static_cast(state->last_dqbuf_index); + state->last_dqbuf_index = -1; + return ioctl(state->fd, VIDIOC_QBUF, &buffer) == 0 ? ERROR_NONE : ERROR_RESOURCE; +} + +uint32_t gc0308_get_width(Gc0308Handle handle) { + if (!handle) return 0; + auto* state = static_cast(handle); + xSemaphoreTake(state->rotation_mutex, portMAX_DELAY); + uint32_t width = state->width; + xSemaphoreGive(state->rotation_mutex); + return width; +} + +uint32_t gc0308_get_height(Gc0308Handle handle) { + if (!handle) return 0; + auto* state = static_cast(handle); + xSemaphoreTake(state->rotation_mutex, portMAX_DELAY); + uint32_t height = state->height; + xSemaphoreGive(state->rotation_mutex); + return height; +} + +error_t gc0308_capture_jpeg(Gc0308Handle handle, uint8_t** out_buf, size_t* out_len, uint8_t quality) { + if (!handle || !out_buf || !out_len) return ERROR_INVALID_ARGUMENT; + auto* state = static_cast(handle); + + // Dequeue one frame. Dimensions come back atomically with the frame data itself, + // so they can't drift if a concurrent gc0308_set_rotation() changes state->width/height. + uint8_t* frame = nullptr; + size_t frame_len = 0; + uint32_t frame_width = 0; + uint32_t frame_height = 0; + error_t err = gc0308_get_frame(handle, &frame, &frame_len, 500, &frame_width, &frame_height); + if (err != ERROR_NONE) return err; + if (frame == nullptr || frame_len == 0) { + gc0308_release_frame(handle); + return ERROR_RESOURCE; + } + + size_t image_size = (size_t)frame_width * frame_height * 2; + uint8_t* aligned_input = static_cast(heap_caps_aligned_alloc(16, image_size, MALLOC_CAP_SPIRAM)); + uint8_t* jpeg_output = static_cast(heap_caps_aligned_alloc(16, image_size, MALLOC_CAP_SPIRAM)); + if (!aligned_input || !jpeg_output) { + heap_caps_free(aligned_input); + heap_caps_free(jpeg_output); + gc0308_release_frame(handle); + return ERROR_OUT_OF_MEMORY; + } + memcpy(aligned_input, frame, frame_len); + if (state->encoder_width != frame_width || state->encoder_height != frame_height) { + if (state->encoder) jpeg_enc_close(state->encoder); + state->encoder = nullptr; + jpeg_enc_config_t cfg = DEFAULT_JPEG_ENC_CONFIG(); + cfg.width = static_cast(frame_width); + cfg.height = static_cast(frame_height); + cfg.src_type = JPEG_PIXEL_FORMAT_RGB565_LE; + cfg.subsampling = JPEG_SUBSAMPLE_420; + cfg.quality = quality; + if (jpeg_enc_open(&cfg, &state->encoder) != JPEG_ERR_OK) { + heap_caps_free(aligned_input); + heap_caps_free(jpeg_output); + gc0308_release_frame(handle); + return ERROR_RESOURCE; + } + state->encoder_width = frame_width; + state->encoder_height = frame_height; + } + jpeg_enc_set_quality(state->encoder, quality); + int encoded_size = 0; + jpeg_error_t encode_error = jpeg_enc_process(state->encoder, aligned_input, static_cast(frame_len), jpeg_output, static_cast(image_size), &encoded_size); + heap_caps_free(aligned_input); + gc0308_release_frame(handle); + + if (encode_error != JPEG_ERR_OK || encoded_size <= 0) { + LOG_E(TAG, "JPEG conversion failed: %d", static_cast(encode_error)); + heap_caps_free(jpeg_output); + return ERROR_RESOURCE; + } + + *out_buf = jpeg_output; + *out_len = encoded_size; + return ERROR_NONE; +} + +CameraApi gc0308_camera_api = { + .open = gc0308_open, + .close = gc0308_close, + .get_frame = gc0308_get_frame, + .release_frame = gc0308_release_frame, + .get_width = gc0308_get_width, + .get_height = gc0308_get_height, + .set_rotation = gc0308_set_rotation, + .capture_jpeg = gc0308_capture_jpeg +}; + +Driver gc0308_driver = { + .name = "gc0308", + .compatible = (const char*[]) { "galaxycore,gc0308", nullptr }, + .start_device = start, + .stop_device = stop, + .api = &gc0308_camera_api, + .device_type = &CAMERA_TYPE, + .owner = &gc0308_module, + .internal = nullptr +}; + +} // extern "C" diff --git a/Drivers/gc0308-module/source/module.cpp b/Drivers/gc0308-module/source/module.cpp new file mode 100644 index 000000000..cd38ec74e --- /dev/null +++ b/Drivers/gc0308-module/source/module.cpp @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +#include +#include + +extern "C" { + +extern Driver gc0308_driver; + +static Driver* const gc0308_drivers[] = { + &gc0308_driver, + nullptr +}; + +Module gc0308_module = { + .name = "gc0308", + .start = nullptr, + .stop = nullptr, + .drivers = gc0308_drivers, + .symbols = nullptr, + .internal = nullptr +}; + +} // extern "C" diff --git a/Drivers/mpu6886-module/bindings/invensense,mpu6886.yaml b/Drivers/mpu6886-module/bindings/invensense,mpu6886.yaml index 9d9a700c2..86c0ae88b 100644 --- a/Drivers/mpu6886-module/bindings/invensense,mpu6886.yaml +++ b/Drivers/mpu6886-module/bindings/invensense,mpu6886.yaml @@ -1,5 +1,31 @@ description: InvenSense (TDK) MPU-6886 6-axis IMU -include: ["i2c-device.yaml"] +include: [ "i2c-device.yaml" ] compatible: "invensense,mpu6886" + +properties: + swap-xy: + type: boolean + default: false + description: Swap the X and Y axes + swap-xz: + type: boolean + default: false + description: Swap the X and Z axes + swap-yz: + type: boolean + default: false + description: Swap the Y and Z axes + invert-x: + type: boolean + default: false + description: Invert the X axis + invert-y: + type: boolean + default: false + description: Invert the Y axis + invert-z: + type: boolean + default: false + description: Invert the Z axis diff --git a/Drivers/mpu6886-module/include/drivers/mpu6886.h b/Drivers/mpu6886-module/include/drivers/mpu6886.h index 1a150d20b..7438a9179 100644 --- a/Drivers/mpu6886-module/include/drivers/mpu6886.h +++ b/Drivers/mpu6886-module/include/drivers/mpu6886.h @@ -14,6 +14,12 @@ extern "C" { struct Mpu6886Config { /** Address on bus */ uint8_t address; + bool swap_xy; + bool swap_xz; + bool swap_yz; + bool invert_x; + bool invert_y; + bool invert_z; }; /** diff --git a/Drivers/mpu6886-module/source/mpu6886.cpp b/Drivers/mpu6886-module/source/mpu6886.cpp index 9d975ab60..32e272803 100644 --- a/Drivers/mpu6886-module/source/mpu6886.cpp +++ b/Drivers/mpu6886-module/source/mpu6886.cpp @@ -5,6 +5,8 @@ #include #include +#include + #define TAG "MPU6886" // Register map @@ -139,9 +141,21 @@ error_t mpu6886_read_accel(Device* device, ImuAccelData* data) { error_t error = i2c_controller_read_register(i2c_controller, address, REG_ACCEL_XOUT_H, buf, sizeof(buf), I2C_TIMEOUT_TICKS); if (error != ERROR_NONE) return error; - data->ax = toI16(buf[0], buf[1]) * ACCEL_SCALE; - data->ay = toI16(buf[2], buf[3]) * ACCEL_SCALE; - data->az = toI16(buf[4], buf[5]) * ACCEL_SCALE; + float axes[3] = { + toI16(buf[0], buf[1]) * ACCEL_SCALE, + toI16(buf[2], buf[3]) * ACCEL_SCALE, + toI16(buf[4], buf[5]) * ACCEL_SCALE, + }; + const auto& config = *GET_CONFIG(device); + if (config.swap_xy) std::swap(axes[0], axes[1]); + if (config.swap_xz) std::swap(axes[0], axes[2]); + if (config.swap_yz) std::swap(axes[1], axes[2]); + if (config.invert_x) axes[0] = -axes[0]; + if (config.invert_y) axes[1] = -axes[1]; + if (config.invert_z) axes[2] = -axes[2]; + data->ax = axes[0]; + data->ay = axes[1]; + data->az = axes[2]; return ERROR_NONE; } diff --git a/Drivers/qmi8658-module/bindings/qst,qmi8658.yaml b/Drivers/qmi8658-module/bindings/qst,qmi8658.yaml index 6182201af..09242f968 100644 --- a/Drivers/qmi8658-module/bindings/qst,qmi8658.yaml +++ b/Drivers/qmi8658-module/bindings/qst,qmi8658.yaml @@ -3,3 +3,29 @@ description: QST QMI8658 6-axis IMU include: [ "i2c-device.yaml" ] compatible: "qst,qmi8658" + +properties: + swap-xy: + type: boolean + default: false + description: Swap the X and Y axes + swap-xz: + type: boolean + default: false + description: Swap the X and Z axes + swap-yz: + type: boolean + default: false + description: Swap the Y and Z axes + invert-x: + type: boolean + default: false + description: Invert the X axis + invert-y: + type: boolean + default: false + description: Invert the Y axis + invert-z: + type: boolean + default: false + description: Invert the Z axis diff --git a/Drivers/qmi8658-module/include/drivers/qmi8658.h b/Drivers/qmi8658-module/include/drivers/qmi8658.h index 69a4badcc..78f13f802 100644 --- a/Drivers/qmi8658-module/include/drivers/qmi8658.h +++ b/Drivers/qmi8658-module/include/drivers/qmi8658.h @@ -14,6 +14,12 @@ extern "C" { struct Qmi8658Config { /** I2C address (0x6A when SA0=low, 0x6B when SA0=high) */ uint8_t address; + bool swap_xy; + bool swap_xz; + bool swap_yz; + bool invert_x; + bool invert_y; + bool invert_z; }; /** diff --git a/Drivers/qmi8658-module/source/qmi8658.cpp b/Drivers/qmi8658-module/source/qmi8658.cpp index 2d6b31991..619669144 100644 --- a/Drivers/qmi8658-module/source/qmi8658.cpp +++ b/Drivers/qmi8658-module/source/qmi8658.cpp @@ -5,6 +5,8 @@ #include #include +#include + #define TAG "QMI8658" // Register map (QMI8658 datasheet) @@ -107,9 +109,21 @@ error_t qmi8658_read_accel(Device* device, ImuAccelData* data) { return static_cast(static_cast(hi) << 8 | lo); }; - data->ax = toI16(buf[0], buf[1]) * ACCEL_SCALE; - data->ay = toI16(buf[2], buf[3]) * ACCEL_SCALE; - data->az = toI16(buf[4], buf[5]) * ACCEL_SCALE; + float axes[3] = { + toI16(buf[0], buf[1]) * ACCEL_SCALE, + toI16(buf[2], buf[3]) * ACCEL_SCALE, + toI16(buf[4], buf[5]) * ACCEL_SCALE, + }; + const auto& config = *GET_CONFIG(device); + if (config.swap_xy) std::swap(axes[0], axes[1]); + if (config.swap_xz) std::swap(axes[0], axes[2]); + if (config.swap_yz) std::swap(axes[1], axes[2]); + if (config.invert_x) axes[0] = -axes[0]; + if (config.invert_y) axes[1] = -axes[1]; + if (config.invert_z) axes[2] = -axes[2]; + data->ax = axes[0]; + data->ay = axes[1]; + data->az = axes[2]; return ERROR_NONE; } diff --git a/Drivers/sc2356-module/bindings/smartsens,sc2356.yaml b/Drivers/sc2356-module/bindings/smartsens,sc2356.yaml index c2bbb71e7..70440c31d 100644 --- a/Drivers/sc2356-module/bindings/smartsens,sc2356.yaml +++ b/Drivers/sc2356-module/bindings/smartsens,sc2356.yaml @@ -5,11 +5,13 @@ include: [ "i2c-device.yaml" ] compatible: "smartsens,sc2356" properties: + rotation-offset: + type: int + enum: [ 0, 90, 180, 270 ] + default: 0 + description: Clockwise mounting correction in degrees (0, 90, 180 or 270). pin-reset: type: phandles default: GPIO_PIN_SPEC_NONE description: > - Reset GPIO pin. Pulsed (asserted then released, with a settle delay) before the sensor is - probed in start_device. Typically wired through a board's IO expander rather than a direct - SoC GPIO, so it's handled here via the generic gpio_descriptor API rather than - esp_video_init's csi_config.reset_pin (which only accepts native SoC GPIOs). + Reset GPIO pin. Pulsed (asserted then released, with a settle delay) before the sensor is probed in start_device. Typically wired through a board's IO expander rather than a direct SoC GPIO, so it's handled here via the generic gpio_descriptor API rather than esp_video_init's csi_config.reset_pin (which only accepts native SoC GPIOs). diff --git a/Drivers/sc2356-module/include/drivers/sc2356.h b/Drivers/sc2356-module/include/drivers/sc2356.h index 897176d7a..11e47b0dc 100644 --- a/Drivers/sc2356-module/include/drivers/sc2356.h +++ b/Drivers/sc2356-module/include/drivers/sc2356.h @@ -18,6 +18,9 @@ struct Sc2356Config { /** SCCB I2C address (0x36) */ uint8_t address; + /** Clockwise mounting correction applied after the requested display rotation. */ + uint16_t rotation_offset; + // Reset pin. GPIO_PIN_SPEC_NONE if the sensor's reset is tied high on the board (or otherwise // not under our control), in which case start_device skips the reset pulse entirely and goes // straight to probing. diff --git a/Drivers/sc2356-module/source/sc2356.cpp b/Drivers/sc2356-module/source/sc2356.cpp index 21c0c1f0e..4b959dbf0 100644 --- a/Drivers/sc2356-module/source/sc2356.cpp +++ b/Drivers/sc2356-module/source/sc2356.cpp @@ -64,6 +64,10 @@ struct Sc2356State : CameraHandleData { #define GET_CONFIG(device) (static_cast((device)->config)) +static CameraRotation add_rotation(CameraRotation rotation, uint16_t offset) { + return static_cast((static_cast(rotation) + offset) % 360); +} + // Reset timings per the SC2356/SC202CS datasheet's power-up sequence: hold reset asserted for at // least 1ms, then wait for the sensor's internal power-on/clock startup (datasheet specifies a // minimum before the first SCCB transaction - 10ms gives comfortable margin) before probing. @@ -390,17 +394,18 @@ error_t sc2356_close(Sc2356Handle handle) { error_t sc2356_set_rotation(Sc2356Handle handle, CameraRotation rotation) { if (!handle) return ERROR_INVALID_ARGUMENT; auto* state = static_cast(handle); + CameraRotation effective_rotation = add_rotation(rotation, GET_CONFIG(state->device)->rotation_offset); xSemaphoreTake(state->rotation_mutex, portMAX_DELAY); - if (state->rotation == rotation) { + if (state->rotation == effective_rotation) { xSemaphoreGive(state->rotation_mutex); return ERROR_NONE; } - bool needs_swap = (rotation == CAMERA_ROTATION_90 || rotation == CAMERA_ROTATION_270); + bool needs_swap = (effective_rotation == CAMERA_ROTATION_90 || effective_rotation == CAMERA_ROTATION_270); - state->rotation = rotation; + state->rotation = effective_rotation; if (needs_swap) { state->width = state->native_height; state->height = state->native_width; diff --git a/Drivers/st7123-module/README.md b/Drivers/st7123-module/README.md index 7afc2d324..408eaeeb8 100644 --- a/Drivers/st7123-module/README.md +++ b/Drivers/st7123-module/README.md @@ -1,5 +1,8 @@ # ST7123 display + touch controller +- Datasheet: https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1132/ST7123_SPEC_Preliminary_V0.5.pdf +- Datasheet: https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1132/ST7123-TDDI-Interface-Protocol-V01.11.pdf.pdf + Two drivers for the Sitronix `ST7123`, sharing this module since they're the same physical chip family and commonly used together: diff --git a/Modules/lvgl-module/assets/generate-all.py b/Modules/lvgl-module/assets/generate-all.py index 1c5dda3fb..3a67c271c 100644 --- a/Modules/lvgl-module/assets/generate-all.py +++ b/Modules/lvgl-module/assets/generate-all.py @@ -111,6 +111,9 @@ def generate_icon_names(codepoint_map: dict, codepoint_names: list, variable_nam "usb", # Power (settings) app "wifi", # WiFi (settings) app "bluetooth", # Bluetooth (settings) app + "terminal", + "logo_dev", + "camera", ] # Get more from https://fonts.google.com/icons?icon.set=Material+Symbols&icon.style=Rounded diff --git a/Modules/lvgl-module/include/lvgl/icons/shared.h b/Modules/lvgl-module/include/lvgl/icons/shared.h index 306b502f1..d2fcbd451 100644 --- a/Modules/lvgl-module/include/lvgl/icons/shared.h +++ b/Modules/lvgl-module/include/lvgl/icons/shared.h @@ -45,3 +45,6 @@ #define LVGL_ICON_SHARED_USB "\xEE\x87\xA0" #define LVGL_ICON_SHARED_WIFI "\xEE\x98\xBE" #define LVGL_ICON_SHARED_BLUETOOTH "\xEE\x86\xA7" +#define LVGL_ICON_SHARED_TERMINAL "\xEE\xAE\x8E" +#define LVGL_ICON_SHARED_LOGO_DEV "\xEE\xAB\x96" +#define LVGL_ICON_SHARED_CAMERA "\xEE\x8E\xAF" diff --git a/Modules/lvgl-module/source-fonts/material_symbols_shared_12.c b/Modules/lvgl-module/source-fonts/material_symbols_shared_12.c index 3b3b751da..8c0c11f52 100644 --- a/Modules/lvgl-module/source-fonts/material_symbols_shared_12.c +++ b/Modules/lvgl-module/source-fonts/material_symbols_shared_12.c @@ -1,7 +1,7 @@ /******************************************************************************* * Size: 12 px * Bpp: 2 - * Opts: --no-compress --no-prefilter --bpp 2 --size 12 --font MaterialSymbolsRounded.ttf -r 0xE145,0xE5C3,0xE770,0xEF40,0xEBCC,0xEFE6,0xEF4A,0xE5CD,0xF15C,0xE668,0xE92E,0xE326,0xEB97,0xE745,0xEC1C,0xE2C7,0xF720,0xF090,0xE8AF,0xE30F,0xE8FD,0xE9F4,0xE3F4,0xE316,0xE90F,0xEA07,0xE9B9,0xE159,0xE5D2,0xE28D,0xE5D4,0xE405,0xE89C,0xF8C7,0xE5D5,0xEF7A,0xE8B8,0xE9F7,0xE55D,0xF028,0xE1E0,0xE63E,0xE1A7 --format lvgl -o ../source-fonts/material_symbols_shared_12.c --force-fast-kern-format + * Opts: --no-compress --no-prefilter --bpp 2 --size 12 --font MaterialSymbolsRounded.ttf -r 0xE145,0xE5C3,0xE770,0xEF40,0xEBCC,0xEFE6,0xEF4A,0xE5CD,0xF15C,0xE668,0xE92E,0xE326,0xEB97,0xE745,0xEC1C,0xE2C7,0xF720,0xF090,0xE8AF,0xE30F,0xE8FD,0xE9F4,0xE3F4,0xE316,0xE90F,0xEA07,0xE9B9,0xE159,0xE5D2,0xE28D,0xE5D4,0xE405,0xE89C,0xF8C7,0xE5D5,0xEF7A,0xE8B8,0xE9F7,0xE55D,0xF028,0xE1E0,0xE63E,0xE1A7,0xEB8E,0xEAD6,0xE3AF --format lvgl -o ../source-fonts/material_symbols_shared_12.c --force-fast-kern-format ******************************************************************************/ #ifdef LV_LVGL_H_INCLUDE_SIMPLE @@ -67,6 +67,12 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { 0x0, 0xc3, 0xa0, 0xc, 0x37, 0xa4, 0xc3, 0x1a, 0x4c, 0x3f, 0xfc, 0xff, + /* U+E3AF "" */ + 0x6, 0xf9, 0x2, 0xd2, 0x78, 0x74, 0x50, 0xd9, + 0x8e, 0xaa, 0xc3, 0xf4, 0x3c, 0x1f, 0xc3, 0xaa, + 0xb2, 0x67, 0x5, 0x1d, 0x2d, 0x87, 0x80, 0x6f, + 0x90, + /* U+E3F4 "" */ 0x15, 0x55, 0xb, 0xaa, 0xac, 0x90, 0x0, 0xc9, 0x0, 0xc, 0x90, 0x0, 0xc9, 0x1, 0xc, 0x97, @@ -178,6 +184,17 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { 0xff, 0xe7, 0x31, 0xcd, 0x2e, 0xab, 0x80, 0x6f, 0x90, + /* U+EAD6 "" */ + 0x15, 0x55, 0x7, 0xaa, 0xac, 0xa0, 0x0, 0x9a, + 0x4, 0x9, 0xa6, 0x86, 0x9a, 0x68, 0xd9, 0xa9, + 0x94, 0x9a, 0x0, 0x9, 0x65, 0x55, 0xd2, 0xaa, + 0xa8, + + /* U+EB8E "" */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x0, 0x3c, + 0xa0, 0x3, 0xc3, 0x40, 0x3c, 0xa1, 0x93, 0xc0, + 0x19, 0x3f, 0xff, 0xff, + /* U+EB97 "" */ 0x15, 0x55, 0x4e, 0xaa, 0xab, 0xc0, 0x0, 0x3c, 0xaa, 0x63, 0xc1, 0x0, 0x3c, 0xdf, 0xf3, 0xc1, @@ -261,39 +278,42 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { {.bitmap_index = 152, .adv_w = 192, .box_w = 6, .box_h = 4, .ofs_x = 3, .ofs_y = 4}, {.bitmap_index = 158, .adv_w = 192, .box_w = 10, .box_h = 8, .ofs_x = 1, .ofs_y = 2}, {.bitmap_index = 178, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 203, .adv_w = 192, .box_w = 6, .box_h = 10, .ofs_x = 3, .ofs_y = 1}, - {.bitmap_index = 218, .adv_w = 192, .box_w = 8, .box_h = 10, .ofs_x = 2, .ofs_y = 1}, - {.bitmap_index = 238, .adv_w = 192, .box_w = 8, .box_h = 8, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 254, .adv_w = 192, .box_w = 8, .box_h = 8, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 270, .adv_w = 192, .box_w = 10, .box_h = 6, .ofs_x = 1, .ofs_y = 3}, - {.bitmap_index = 285, .adv_w = 192, .box_w = 2, .box_h = 8, .ofs_x = 5, .ofs_y = 2}, - {.bitmap_index = 289, .adv_w = 192, .box_w = 8, .box_h = 8, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 305, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 1}, - {.bitmap_index = 332, .adv_w = 192, .box_w = 8, .box_h = 6, .ofs_x = 2, .ofs_y = 3}, - {.bitmap_index = 344, .adv_w = 192, .box_w = 9, .box_h = 8, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 362, .adv_w = 192, .box_w = 10, .box_h = 9, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 385, .adv_w = 192, .box_w = 8, .box_h = 10, .ofs_x = 2, .ofs_y = 1}, - {.bitmap_index = 405, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 203, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 228, .adv_w = 192, .box_w = 6, .box_h = 10, .ofs_x = 3, .ofs_y = 1}, + {.bitmap_index = 243, .adv_w = 192, .box_w = 8, .box_h = 10, .ofs_x = 2, .ofs_y = 1}, + {.bitmap_index = 263, .adv_w = 192, .box_w = 8, .box_h = 8, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 279, .adv_w = 192, .box_w = 8, .box_h = 8, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 295, .adv_w = 192, .box_w = 10, .box_h = 6, .ofs_x = 1, .ofs_y = 3}, + {.bitmap_index = 310, .adv_w = 192, .box_w = 2, .box_h = 8, .ofs_x = 5, .ofs_y = 2}, + {.bitmap_index = 314, .adv_w = 192, .box_w = 8, .box_h = 8, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 330, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 1}, + {.bitmap_index = 357, .adv_w = 192, .box_w = 8, .box_h = 6, .ofs_x = 2, .ofs_y = 3}, + {.bitmap_index = 369, .adv_w = 192, .box_w = 9, .box_h = 8, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 387, .adv_w = 192, .box_w = 10, .box_h = 9, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 410, .adv_w = 192, .box_w = 8, .box_h = 10, .ofs_x = 2, .ofs_y = 1}, {.bitmap_index = 430, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, {.bitmap_index = 455, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 480, .adv_w = 192, .box_w = 8, .box_h = 10, .ofs_x = 2, .ofs_y = 1}, - {.bitmap_index = 500, .adv_w = 192, .box_w = 8, .box_h = 10, .ofs_x = 2, .ofs_y = 1}, - {.bitmap_index = 520, .adv_w = 192, .box_w = 10, .box_h = 8, .ofs_x = 1, .ofs_y = 2}, - {.bitmap_index = 540, .adv_w = 192, .box_w = 12, .box_h = 12, .ofs_x = 0, .ofs_y = 0}, - {.bitmap_index = 576, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 480, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 505, .adv_w = 192, .box_w = 8, .box_h = 10, .ofs_x = 2, .ofs_y = 1}, + {.bitmap_index = 525, .adv_w = 192, .box_w = 8, .box_h = 10, .ofs_x = 2, .ofs_y = 1}, + {.bitmap_index = 545, .adv_w = 192, .box_w = 10, .box_h = 8, .ofs_x = 1, .ofs_y = 2}, + {.bitmap_index = 565, .adv_w = 192, .box_w = 12, .box_h = 12, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 601, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, {.bitmap_index = 626, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, {.bitmap_index = 651, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 676, .adv_w = 192, .box_w = 8, .box_h = 10, .ofs_x = 2, .ofs_y = 1}, - {.bitmap_index = 696, .adv_w = 192, .box_w = 9, .box_h = 8, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 714, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 739, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 764, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 789, .adv_w = 192, .box_w = 12, .box_h = 8, .ofs_x = 0, .ofs_y = 2}, - {.bitmap_index = 813, .adv_w = 192, .box_w = 8, .box_h = 8, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 829, .adv_w = 192, .box_w = 12, .box_h = 8, .ofs_x = 0, .ofs_y = 2}, - {.bitmap_index = 853, .adv_w = 192, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 876, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1} + {.bitmap_index = 676, .adv_w = 192, .box_w = 10, .box_h = 8, .ofs_x = 1, .ofs_y = 2}, + {.bitmap_index = 696, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 721, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 746, .adv_w = 192, .box_w = 8, .box_h = 10, .ofs_x = 2, .ofs_y = 1}, + {.bitmap_index = 766, .adv_w = 192, .box_w = 9, .box_h = 8, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 784, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 809, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 834, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 859, .adv_w = 192, .box_w = 12, .box_h = 8, .ofs_x = 0, .ofs_y = 2}, + {.bitmap_index = 883, .adv_w = 192, .box_w = 8, .box_h = 8, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 899, .adv_w = 192, .box_w = 12, .box_h = 8, .ofs_x = 0, .ofs_y = 2}, + {.bitmap_index = 923, .adv_w = 192, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 946, .adv_w = 192, .box_w = 10, .box_h = 10, .ofs_x = 1, .ofs_y = 1} }; /*--------------------- @@ -302,11 +322,11 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { static const uint16_t unicode_list_0[] = { 0x0, 0x14, 0x62, 0x9b, 0x148, 0x182, 0x1ca, 0x1d1, - 0x1e1, 0x2af, 0x2c0, 0x418, 0x47e, 0x488, 0x48d, 0x48f, - 0x490, 0x4f9, 0x523, 0x600, 0x62b, 0x757, 0x76a, 0x773, - 0x7b8, 0x7ca, 0x7e9, 0x874, 0x8af, 0x8b2, 0x8c2, 0xa52, - 0xa87, 0xad7, 0xdfb, 0xe05, 0xe35, 0xea1, 0xee3, 0xf4b, - 0x1017, 0x15db, 0x1782 + 0x1e1, 0x26a, 0x2af, 0x2c0, 0x418, 0x47e, 0x488, 0x48d, + 0x48f, 0x490, 0x4f9, 0x523, 0x600, 0x62b, 0x757, 0x76a, + 0x773, 0x7b8, 0x7ca, 0x7e9, 0x874, 0x8af, 0x8b2, 0x8c2, + 0x991, 0xa49, 0xa52, 0xa87, 0xad7, 0xdfb, 0xe05, 0xe35, + 0xea1, 0xee3, 0xf4b, 0x1017, 0x15db, 0x1782 }; /*Collect the unicode lists and glyph_id offsets*/ @@ -314,7 +334,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] = { { .range_start = 57669, .range_length = 6019, .glyph_id_start = 1, - .unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 43, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + .unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 46, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY } }; diff --git a/Modules/lvgl-module/source-fonts/material_symbols_shared_16.c b/Modules/lvgl-module/source-fonts/material_symbols_shared_16.c index 6add96160..dad2feff7 100644 --- a/Modules/lvgl-module/source-fonts/material_symbols_shared_16.c +++ b/Modules/lvgl-module/source-fonts/material_symbols_shared_16.c @@ -1,7 +1,7 @@ /******************************************************************************* * Size: 16 px * Bpp: 2 - * Opts: --no-compress --no-prefilter --bpp 2 --size 16 --font MaterialSymbolsRounded.ttf -r 0xE145,0xE5C3,0xE770,0xEF40,0xEBCC,0xEFE6,0xEF4A,0xE5CD,0xF15C,0xE668,0xE92E,0xE326,0xEB97,0xE745,0xEC1C,0xE2C7,0xF720,0xF090,0xE8AF,0xE30F,0xE8FD,0xE9F4,0xE3F4,0xE316,0xE90F,0xEA07,0xE9B9,0xE159,0xE5D2,0xE28D,0xE5D4,0xE405,0xE89C,0xF8C7,0xE5D5,0xEF7A,0xE8B8,0xE9F7,0xE55D,0xF028,0xE1E0,0xE63E,0xE1A7 --format lvgl -o ../source-fonts/material_symbols_shared_16.c --force-fast-kern-format + * Opts: --no-compress --no-prefilter --bpp 2 --size 16 --font MaterialSymbolsRounded.ttf -r 0xE145,0xE5C3,0xE770,0xEF40,0xEBCC,0xEFE6,0xEF4A,0xE5CD,0xF15C,0xE668,0xE92E,0xE326,0xEB97,0xE745,0xEC1C,0xE2C7,0xF720,0xF090,0xE8AF,0xE30F,0xE8FD,0xE9F4,0xE3F4,0xE316,0xE90F,0xEA07,0xE9B9,0xE159,0xE5D2,0xE28D,0xE5D4,0xE405,0xE89C,0xF8C7,0xE5D5,0xEF7A,0xE8B8,0xE9F7,0xE55D,0xF028,0xE1E0,0xE63E,0xE1A7,0xEB8E,0xEAD6,0xE3AF --format lvgl -o ../source-fonts/material_symbols_shared_16.c --force-fast-kern-format ******************************************************************************/ #ifdef LV_LVGL_H_INCLUDE_SIMPLE @@ -88,6 +88,15 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { 0x55, 0xd, 0xd, 0xbf, 0xfc, 0xff, 0xd1, 0x55, 0x1, 0x54, + /* U+E3AF "" */ + 0x0, 0x15, 0x40, 0x0, 0x1f, 0xff, 0x40, 0xb, + 0x42, 0x2e, 0x2, 0xd0, 0x50, 0x34, 0x36, 0xc, + 0x1, 0xcb, 0x1a, 0xfe, 0xad, 0xa0, 0xbf, 0xc0, + 0xea, 0x3, 0xfd, 0xe, 0xba, 0xbf, 0x60, 0xd7, + 0x0, 0x30, 0x9c, 0x3c, 0x9, 0xb, 0x80, 0xf4, + 0x80, 0xf0, 0x3, 0xfa, 0xfc, 0x0, 0x6, 0xf9, + 0x0, + /* U+E3F4 "" */ 0xbf, 0xff, 0xfe, 0xe5, 0x55, 0x5b, 0xd0, 0x0, 0x7, 0xd0, 0x0, 0x7, 0xd0, 0x0, 0x7, 0xd0, @@ -245,6 +254,21 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { 0x5c, 0xf0, 0x3, 0xff, 0xfc, 0x0, 0x6, 0xf9, 0x0, + /* U+EAD6 "" */ + 0xbf, 0xff, 0xfe, 0xe5, 0x55, 0x5b, 0xd0, 0x0, + 0x7, 0xd0, 0x0, 0x7, 0xdf, 0x7a, 0x27, 0xdc, + 0xa1, 0x77, 0xdc, 0xa0, 0xe7, 0xdf, 0x78, 0xc7, + 0xd0, 0x0, 0x7, 0xd0, 0x0, 0x7, 0xe5, 0x55, + 0x5b, 0xbf, 0xff, 0xfe, + + /* U+EB8E "" */ + 0x2a, 0xaa, 0xaa, 0x4f, 0xff, 0xff, 0xfd, 0xff, + 0xff, 0xff, 0xde, 0x0, 0x0, 0xd, 0xd2, 0x40, + 0x0, 0xdd, 0x1d, 0x0, 0xd, 0xd0, 0xb0, 0x0, + 0xdd, 0x1e, 0x0, 0xd, 0xd2, 0x83, 0xf8, 0xdd, + 0x0, 0x15, 0xd, 0xbf, 0xff, 0xff, 0xd1, 0x55, + 0x55, 0x50, + /* U+EB97 "" */ 0x7f, 0xff, 0xff, 0xce, 0x55, 0x55, 0x5d, 0xd0, 0x0, 0x0, 0xdd, 0x15, 0x53, 0x4d, 0xd1, 0x55, @@ -356,40 +380,43 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { {.bitmap_index = 240, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = 1}, {.bitmap_index = 289, .adv_w = 256, .box_w = 8, .box_h = 5, .ofs_x = 4, .ofs_y = 6}, {.bitmap_index = 299, .adv_w = 256, .box_w = 14, .box_h = 12, .ofs_x = 1, .ofs_y = 2}, - {.bitmap_index = 341, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 377, .adv_w = 256, .box_w = 8, .box_h = 12, .ofs_x = 4, .ofs_y = 2}, - {.bitmap_index = 401, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 437, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 473, .adv_w = 256, .box_w = 10, .box_h = 10, .ofs_x = 3, .ofs_y = 3}, - {.bitmap_index = 498, .adv_w = 256, .box_w = 12, .box_h = 8, .ofs_x = 2, .ofs_y = 4}, - {.bitmap_index = 522, .adv_w = 256, .box_w = 4, .box_h = 12, .ofs_x = 6, .ofs_y = 2}, - {.bitmap_index = 534, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 570, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 2}, - {.bitmap_index = 618, .adv_w = 256, .box_w = 12, .box_h = 8, .ofs_x = 2, .ofs_y = 4}, - {.bitmap_index = 642, .adv_w = 256, .box_w = 13, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 681, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 717, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = 1}, - {.bitmap_index = 759, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 1, .ofs_y = 2}, - {.bitmap_index = 805, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 341, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 390, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 426, .adv_w = 256, .box_w = 8, .box_h = 12, .ofs_x = 4, .ofs_y = 2}, + {.bitmap_index = 450, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 486, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 522, .adv_w = 256, .box_w = 10, .box_h = 10, .ofs_x = 3, .ofs_y = 3}, + {.bitmap_index = 547, .adv_w = 256, .box_w = 12, .box_h = 8, .ofs_x = 2, .ofs_y = 4}, + {.bitmap_index = 571, .adv_w = 256, .box_w = 4, .box_h = 12, .ofs_x = 6, .ofs_y = 2}, + {.bitmap_index = 583, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 619, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 2}, + {.bitmap_index = 667, .adv_w = 256, .box_w = 12, .box_h = 8, .ofs_x = 2, .ofs_y = 4}, + {.bitmap_index = 691, .adv_w = 256, .box_w = 13, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 730, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 766, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = 1}, + {.bitmap_index = 808, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 1, .ofs_y = 2}, {.bitmap_index = 854, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 903, .adv_w = 256, .box_w = 10, .box_h = 14, .ofs_x = 3, .ofs_y = 1}, - {.bitmap_index = 938, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 974, .adv_w = 256, .box_w = 14, .box_h = 12, .ofs_x = 1, .ofs_y = 2}, - {.bitmap_index = 1016, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = 0}, - {.bitmap_index = 1080, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 1116, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 1165, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 1211, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = 1}, - {.bitmap_index = 1253, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = 1}, - {.bitmap_index = 1295, .adv_w = 256, .box_w = 13, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 1334, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 1383, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 1419, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 1455, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 2}, - {.bitmap_index = 1503, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 1539, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 2}, - {.bitmap_index = 1587, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = 1}, - {.bitmap_index = 1629, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = 1} + {.bitmap_index = 903, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 952, .adv_w = 256, .box_w = 10, .box_h = 14, .ofs_x = 3, .ofs_y = 1}, + {.bitmap_index = 987, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 1023, .adv_w = 256, .box_w = 14, .box_h = 12, .ofs_x = 1, .ofs_y = 2}, + {.bitmap_index = 1065, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 1129, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 1165, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 1214, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 1250, .adv_w = 256, .box_w = 14, .box_h = 12, .ofs_x = 1, .ofs_y = 2}, + {.bitmap_index = 1292, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 1338, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = 1}, + {.bitmap_index = 1380, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = 1}, + {.bitmap_index = 1422, .adv_w = 256, .box_w = 13, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 1461, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 1510, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 1546, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 1582, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 2}, + {.bitmap_index = 1630, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 1666, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 2}, + {.bitmap_index = 1714, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = 1}, + {.bitmap_index = 1756, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = 1} }; /*--------------------- @@ -398,11 +425,11 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { static const uint16_t unicode_list_0[] = { 0x0, 0x14, 0x62, 0x9b, 0x148, 0x182, 0x1ca, 0x1d1, - 0x1e1, 0x2af, 0x2c0, 0x418, 0x47e, 0x488, 0x48d, 0x48f, - 0x490, 0x4f9, 0x523, 0x600, 0x62b, 0x757, 0x76a, 0x773, - 0x7b8, 0x7ca, 0x7e9, 0x874, 0x8af, 0x8b2, 0x8c2, 0xa52, - 0xa87, 0xad7, 0xdfb, 0xe05, 0xe35, 0xea1, 0xee3, 0xf4b, - 0x1017, 0x15db, 0x1782 + 0x1e1, 0x26a, 0x2af, 0x2c0, 0x418, 0x47e, 0x488, 0x48d, + 0x48f, 0x490, 0x4f9, 0x523, 0x600, 0x62b, 0x757, 0x76a, + 0x773, 0x7b8, 0x7ca, 0x7e9, 0x874, 0x8af, 0x8b2, 0x8c2, + 0x991, 0xa49, 0xa52, 0xa87, 0xad7, 0xdfb, 0xe05, 0xe35, + 0xea1, 0xee3, 0xf4b, 0x1017, 0x15db, 0x1782 }; /*Collect the unicode lists and glyph_id offsets*/ @@ -410,7 +437,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] = { { .range_start = 57669, .range_length = 6019, .glyph_id_start = 1, - .unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 43, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + .unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 46, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY } }; diff --git a/Modules/lvgl-module/source-fonts/material_symbols_shared_20.c b/Modules/lvgl-module/source-fonts/material_symbols_shared_20.c index 7ff4f44f1..11eb44f8f 100644 --- a/Modules/lvgl-module/source-fonts/material_symbols_shared_20.c +++ b/Modules/lvgl-module/source-fonts/material_symbols_shared_20.c @@ -1,7 +1,7 @@ /******************************************************************************* * Size: 20 px * Bpp: 2 - * Opts: --no-compress --no-prefilter --bpp 2 --size 20 --font MaterialSymbolsRounded.ttf -r 0xE145,0xE5C3,0xE770,0xEF40,0xEBCC,0xEFE6,0xEF4A,0xE5CD,0xF15C,0xE668,0xE92E,0xE326,0xEB97,0xE745,0xEC1C,0xE2C7,0xF720,0xF090,0xE8AF,0xE30F,0xE8FD,0xE9F4,0xE3F4,0xE316,0xE90F,0xEA07,0xE9B9,0xE159,0xE5D2,0xE28D,0xE5D4,0xE405,0xE89C,0xF8C7,0xE5D5,0xEF7A,0xE8B8,0xE9F7,0xE55D,0xF028,0xE1E0,0xE63E,0xE1A7 --format lvgl -o ../source-fonts/material_symbols_shared_20.c --force-fast-kern-format + * Opts: --no-compress --no-prefilter --bpp 2 --size 20 --font MaterialSymbolsRounded.ttf -r 0xE145,0xE5C3,0xE770,0xEF40,0xEBCC,0xEFE6,0xEF4A,0xE5CD,0xF15C,0xE668,0xE92E,0xE326,0xEB97,0xE745,0xEC1C,0xE2C7,0xF720,0xF090,0xE8AF,0xE30F,0xE8FD,0xE9F4,0xE3F4,0xE316,0xE90F,0xEA07,0xE9B9,0xE159,0xE5D2,0xE28D,0xE5D4,0xE405,0xE89C,0xF8C7,0xE5D5,0xEF7A,0xE8B8,0xE9F7,0xE55D,0xF028,0xE1E0,0xE63E,0xE1A7,0xEB8E,0xEAD6,0xE3AF --format lvgl -o ../source-fonts/material_symbols_shared_20.c --force-fast-kern-format ******************************************************************************/ #ifdef LV_LVGL_H_INCLUDE_SIMPLE @@ -107,6 +107,19 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { 0x80, 0xe0, 0x0, 0x0, 0x38, 0xe, 0x3f, 0xff, 0xc3, 0xff, 0xe2, 0xaa, 0xa4, 0x1a, 0xa8, + /* U+E3AF "" */ + 0x0, 0x1, 0x54, 0x0, 0x0, 0x2, 0xff, 0xfc, + 0x0, 0x1, 0xf9, 0x1e, 0xf4, 0x0, 0x7c, 0x2, + 0x3, 0xd0, 0xf, 0x80, 0x20, 0xf, 0x2, 0xd8, + 0x8, 0x0, 0x38, 0x3c, 0x21, 0xea, 0xab, 0xc3, + 0x82, 0x7f, 0xd0, 0x1d, 0x34, 0xf, 0xff, 0x1, + 0xd3, 0x40, 0xbf, 0xf4, 0x1d, 0x38, 0x7, 0xfc, + 0xc1, 0xc3, 0xea, 0xab, 0x46, 0x3c, 0x1d, 0x0, + 0x20, 0x3b, 0x80, 0xb0, 0x5, 0x1, 0xf0, 0x3, + 0xc0, 0x80, 0x7c, 0x0, 0xb, 0xaa, 0xbf, 0x0, + 0x0, 0x1b, 0xfe, 0x40, 0x0, 0x0, 0x0, 0x0, + 0x0, + /* U+E3F4 "" */ 0x1a, 0xaa, 0xaa, 0xa4, 0x7f, 0xff, 0xff, 0xfd, 0xb0, 0x0, 0x0, 0xe, 0xb0, 0x0, 0x0, 0xe, @@ -320,6 +333,26 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { 0x0, 0x1b, 0xfe, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, + /* U+EAD6 "" */ + 0x1a, 0xaa, 0xaa, 0xa4, 0x7f, 0xff, 0xff, 0xfd, + 0x70, 0x0, 0x0, 0xe, 0xb0, 0x0, 0x0, 0xe, + 0xb0, 0x0, 0x0, 0xe, 0xb2, 0x92, 0x84, 0x4e, + 0xb3, 0xbb, 0x8c, 0xce, 0xb3, 0x3a, 0xc, 0xce, + 0xb3, 0x3b, 0x4a, 0x8e, 0xb3, 0x3a, 0x7, 0x4e, + 0xb2, 0x92, 0x82, 0xe, 0xb0, 0x0, 0x0, 0xe, + 0xb0, 0x0, 0x0, 0xe, 0x70, 0x0, 0x0, 0xe, + 0x7f, 0xff, 0xff, 0xfd, 0x1a, 0xaa, 0xaa, 0xa4, + + /* U+EB8E "" */ + 0x5, 0x55, 0x55, 0x55, 0x43, 0xff, 0xff, 0xff, + 0xfd, 0x3f, 0xff, 0xff, 0xff, 0xe3, 0xff, 0xff, + 0xff, 0xfe, 0x38, 0x0, 0x0, 0x0, 0xe3, 0x87, + 0x40, 0x0, 0xe, 0x38, 0x3d, 0x0, 0x0, 0xe3, + 0x80, 0xf4, 0x0, 0xe, 0x38, 0xf, 0x0, 0x0, + 0xe3, 0x83, 0xc0, 0x69, 0xe, 0x38, 0x20, 0x2f, + 0xf0, 0xe3, 0x80, 0x0, 0x0, 0xe, 0x3f, 0xff, + 0xff, 0xff, 0xd1, 0xaa, 0xaa, 0xaa, 0xa8, + /* U+EB97 "" */ 0xa, 0xaa, 0xaa, 0xaa, 0x43, 0xff, 0xff, 0xff, 0xfd, 0x38, 0x0, 0x0, 0x0, 0xe3, 0x80, 0x0, @@ -466,40 +499,43 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { {.bitmap_index = 355, .adv_w = 320, .box_w = 18, .box_h = 18, .ofs_x = 1, .ofs_y = 1}, {.bitmap_index = 436, .adv_w = 320, .box_w = 10, .box_h = 6, .ofs_x = 5, .ofs_y = 7}, {.bitmap_index = 451, .adv_w = 320, .box_w = 18, .box_h = 14, .ofs_x = 1, .ofs_y = 3}, - {.bitmap_index = 514, .adv_w = 320, .box_w = 16, .box_h = 16, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 578, .adv_w = 320, .box_w = 10, .box_h = 16, .ofs_x = 5, .ofs_y = 2}, - {.bitmap_index = 618, .adv_w = 320, .box_w = 14, .box_h = 16, .ofs_x = 3, .ofs_y = 2}, - {.bitmap_index = 674, .adv_w = 320, .box_w = 14, .box_h = 14, .ofs_x = 3, .ofs_y = 3}, - {.bitmap_index = 723, .adv_w = 320, .box_w = 12, .box_h = 12, .ofs_x = 4, .ofs_y = 4}, - {.bitmap_index = 759, .adv_w = 320, .box_w = 16, .box_h = 10, .ofs_x = 2, .ofs_y = 5}, - {.bitmap_index = 799, .adv_w = 320, .box_w = 4, .box_h = 14, .ofs_x = 8, .ofs_y = 3}, - {.bitmap_index = 813, .adv_w = 320, .box_w = 14, .box_h = 14, .ofs_x = 3, .ofs_y = 3}, - {.bitmap_index = 862, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 2}, - {.bitmap_index = 937, .adv_w = 320, .box_w = 14, .box_h = 10, .ofs_x = 3, .ofs_y = 5}, - {.bitmap_index = 972, .adv_w = 320, .box_w = 16, .box_h = 14, .ofs_x = 3, .ofs_y = 3}, - {.bitmap_index = 1028, .adv_w = 320, .box_w = 16, .box_h = 14, .ofs_x = 2, .ofs_y = 3}, - {.bitmap_index = 1084, .adv_w = 320, .box_w = 14, .box_h = 18, .ofs_x = 3, .ofs_y = 1}, - {.bitmap_index = 1147, .adv_w = 320, .box_w = 18, .box_h = 17, .ofs_x = 1, .ofs_y = 2}, - {.bitmap_index = 1224, .adv_w = 320, .box_w = 17, .box_h = 18, .ofs_x = 2, .ofs_y = 1}, - {.bitmap_index = 1301, .adv_w = 320, .box_w = 18, .box_h = 18, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 1382, .adv_w = 320, .box_w = 14, .box_h = 18, .ofs_x = 3, .ofs_y = 1}, - {.bitmap_index = 1445, .adv_w = 320, .box_w = 14, .box_h = 16, .ofs_x = 3, .ofs_y = 2}, - {.bitmap_index = 1501, .adv_w = 320, .box_w = 18, .box_h = 14, .ofs_x = 1, .ofs_y = 3}, - {.bitmap_index = 1564, .adv_w = 320, .box_w = 20, .box_h = 20, .ofs_x = 0, .ofs_y = 0}, - {.bitmap_index = 1664, .adv_w = 320, .box_w = 16, .box_h = 16, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 1728, .adv_w = 320, .box_w = 18, .box_h = 18, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 1809, .adv_w = 320, .box_w = 18, .box_h = 16, .ofs_x = 1, .ofs_y = 2}, - {.bitmap_index = 1881, .adv_w = 320, .box_w = 16, .box_h = 18, .ofs_x = 2, .ofs_y = 1}, - {.bitmap_index = 1953, .adv_w = 320, .box_w = 14, .box_h = 18, .ofs_x = 3, .ofs_y = 1}, - {.bitmap_index = 2016, .adv_w = 320, .box_w = 16, .box_h = 14, .ofs_x = 3, .ofs_y = 3}, - {.bitmap_index = 2072, .adv_w = 320, .box_w = 18, .box_h = 18, .ofs_x = 1, .ofs_y = 1}, - {.bitmap_index = 2153, .adv_w = 320, .box_w = 16, .box_h = 15, .ofs_x = 2, .ofs_y = 3}, - {.bitmap_index = 2213, .adv_w = 320, .box_w = 16, .box_h = 16, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 2277, .adv_w = 320, .box_w = 20, .box_h = 14, .ofs_x = 0, .ofs_y = 3}, - {.bitmap_index = 2347, .adv_w = 320, .box_w = 14, .box_h = 14, .ofs_x = 3, .ofs_y = 3}, - {.bitmap_index = 2396, .adv_w = 320, .box_w = 20, .box_h = 14, .ofs_x = 0, .ofs_y = 3}, - {.bitmap_index = 2466, .adv_w = 320, .box_w = 16, .box_h = 18, .ofs_x = 2, .ofs_y = 1}, - {.bitmap_index = 2538, .adv_w = 320, .box_w = 18, .box_h = 18, .ofs_x = 1, .ofs_y = 1} + {.bitmap_index = 514, .adv_w = 320, .box_w = 18, .box_h = 18, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 595, .adv_w = 320, .box_w = 16, .box_h = 16, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 659, .adv_w = 320, .box_w = 10, .box_h = 16, .ofs_x = 5, .ofs_y = 2}, + {.bitmap_index = 699, .adv_w = 320, .box_w = 14, .box_h = 16, .ofs_x = 3, .ofs_y = 2}, + {.bitmap_index = 755, .adv_w = 320, .box_w = 14, .box_h = 14, .ofs_x = 3, .ofs_y = 3}, + {.bitmap_index = 804, .adv_w = 320, .box_w = 12, .box_h = 12, .ofs_x = 4, .ofs_y = 4}, + {.bitmap_index = 840, .adv_w = 320, .box_w = 16, .box_h = 10, .ofs_x = 2, .ofs_y = 5}, + {.bitmap_index = 880, .adv_w = 320, .box_w = 4, .box_h = 14, .ofs_x = 8, .ofs_y = 3}, + {.bitmap_index = 894, .adv_w = 320, .box_w = 14, .box_h = 14, .ofs_x = 3, .ofs_y = 3}, + {.bitmap_index = 943, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 2}, + {.bitmap_index = 1018, .adv_w = 320, .box_w = 14, .box_h = 10, .ofs_x = 3, .ofs_y = 5}, + {.bitmap_index = 1053, .adv_w = 320, .box_w = 16, .box_h = 14, .ofs_x = 3, .ofs_y = 3}, + {.bitmap_index = 1109, .adv_w = 320, .box_w = 16, .box_h = 14, .ofs_x = 2, .ofs_y = 3}, + {.bitmap_index = 1165, .adv_w = 320, .box_w = 14, .box_h = 18, .ofs_x = 3, .ofs_y = 1}, + {.bitmap_index = 1228, .adv_w = 320, .box_w = 18, .box_h = 17, .ofs_x = 1, .ofs_y = 2}, + {.bitmap_index = 1305, .adv_w = 320, .box_w = 17, .box_h = 18, .ofs_x = 2, .ofs_y = 1}, + {.bitmap_index = 1382, .adv_w = 320, .box_w = 18, .box_h = 18, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 1463, .adv_w = 320, .box_w = 14, .box_h = 18, .ofs_x = 3, .ofs_y = 1}, + {.bitmap_index = 1526, .adv_w = 320, .box_w = 14, .box_h = 16, .ofs_x = 3, .ofs_y = 2}, + {.bitmap_index = 1582, .adv_w = 320, .box_w = 18, .box_h = 14, .ofs_x = 1, .ofs_y = 3}, + {.bitmap_index = 1645, .adv_w = 320, .box_w = 20, .box_h = 20, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 1745, .adv_w = 320, .box_w = 16, .box_h = 16, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 1809, .adv_w = 320, .box_w = 18, .box_h = 18, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 1890, .adv_w = 320, .box_w = 16, .box_h = 16, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 1954, .adv_w = 320, .box_w = 18, .box_h = 14, .ofs_x = 1, .ofs_y = 3}, + {.bitmap_index = 2017, .adv_w = 320, .box_w = 18, .box_h = 16, .ofs_x = 1, .ofs_y = 2}, + {.bitmap_index = 2089, .adv_w = 320, .box_w = 16, .box_h = 18, .ofs_x = 2, .ofs_y = 1}, + {.bitmap_index = 2161, .adv_w = 320, .box_w = 14, .box_h = 18, .ofs_x = 3, .ofs_y = 1}, + {.bitmap_index = 2224, .adv_w = 320, .box_w = 16, .box_h = 14, .ofs_x = 3, .ofs_y = 3}, + {.bitmap_index = 2280, .adv_w = 320, .box_w = 18, .box_h = 18, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 2361, .adv_w = 320, .box_w = 16, .box_h = 15, .ofs_x = 2, .ofs_y = 3}, + {.bitmap_index = 2421, .adv_w = 320, .box_w = 16, .box_h = 16, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 2485, .adv_w = 320, .box_w = 20, .box_h = 14, .ofs_x = 0, .ofs_y = 3}, + {.bitmap_index = 2555, .adv_w = 320, .box_w = 14, .box_h = 14, .ofs_x = 3, .ofs_y = 3}, + {.bitmap_index = 2604, .adv_w = 320, .box_w = 20, .box_h = 14, .ofs_x = 0, .ofs_y = 3}, + {.bitmap_index = 2674, .adv_w = 320, .box_w = 16, .box_h = 18, .ofs_x = 2, .ofs_y = 1}, + {.bitmap_index = 2746, .adv_w = 320, .box_w = 18, .box_h = 18, .ofs_x = 1, .ofs_y = 1} }; /*--------------------- @@ -508,11 +544,11 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { static const uint16_t unicode_list_0[] = { 0x0, 0x14, 0x62, 0x9b, 0x148, 0x182, 0x1ca, 0x1d1, - 0x1e1, 0x2af, 0x2c0, 0x418, 0x47e, 0x488, 0x48d, 0x48f, - 0x490, 0x4f9, 0x523, 0x600, 0x62b, 0x757, 0x76a, 0x773, - 0x7b8, 0x7ca, 0x7e9, 0x874, 0x8af, 0x8b2, 0x8c2, 0xa52, - 0xa87, 0xad7, 0xdfb, 0xe05, 0xe35, 0xea1, 0xee3, 0xf4b, - 0x1017, 0x15db, 0x1782 + 0x1e1, 0x26a, 0x2af, 0x2c0, 0x418, 0x47e, 0x488, 0x48d, + 0x48f, 0x490, 0x4f9, 0x523, 0x600, 0x62b, 0x757, 0x76a, + 0x773, 0x7b8, 0x7ca, 0x7e9, 0x874, 0x8af, 0x8b2, 0x8c2, + 0x991, 0xa49, 0xa52, 0xa87, 0xad7, 0xdfb, 0xe05, 0xe35, + 0xea1, 0xee3, 0xf4b, 0x1017, 0x15db, 0x1782 }; /*Collect the unicode lists and glyph_id offsets*/ @@ -520,7 +556,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] = { { .range_start = 57669, .range_length = 6019, .glyph_id_start = 1, - .unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 43, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + .unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 46, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY } }; diff --git a/Modules/lvgl-module/source-fonts/material_symbols_shared_24.c b/Modules/lvgl-module/source-fonts/material_symbols_shared_24.c index ca1c60f73..18ca9baeb 100644 --- a/Modules/lvgl-module/source-fonts/material_symbols_shared_24.c +++ b/Modules/lvgl-module/source-fonts/material_symbols_shared_24.c @@ -1,7 +1,7 @@ /******************************************************************************* * Size: 24 px * Bpp: 2 - * Opts: --no-compress --no-prefilter --bpp 2 --size 24 --font MaterialSymbolsRounded.ttf -r 0xE145,0xE5C3,0xE770,0xEF40,0xEBCC,0xEFE6,0xEF4A,0xE5CD,0xF15C,0xE668,0xE92E,0xE326,0xEB97,0xE745,0xEC1C,0xE2C7,0xF720,0xF090,0xE8AF,0xE30F,0xE8FD,0xE9F4,0xE3F4,0xE316,0xE90F,0xEA07,0xE9B9,0xE159,0xE5D2,0xE28D,0xE5D4,0xE405,0xE89C,0xF8C7,0xE5D5,0xEF7A,0xE8B8,0xE9F7,0xE55D,0xF028,0xE1E0,0xE63E,0xE1A7 --format lvgl -o ../source-fonts/material_symbols_shared_24.c --force-fast-kern-format + * Opts: --no-compress --no-prefilter --bpp 2 --size 24 --font MaterialSymbolsRounded.ttf -r 0xE145,0xE5C3,0xE770,0xEF40,0xEBCC,0xEFE6,0xEF4A,0xE5CD,0xF15C,0xE668,0xE92E,0xE326,0xEB97,0xE745,0xEC1C,0xE2C7,0xF720,0xF090,0xE8AF,0xE30F,0xE8FD,0xE9F4,0xE3F4,0xE316,0xE90F,0xEA07,0xE9B9,0xE159,0xE5D2,0xE28D,0xE5D4,0xE405,0xE89C,0xF8C7,0xE5D5,0xEF7A,0xE8B8,0xE9F7,0xE55D,0xF028,0xE1E0,0xE63E,0xE1A7,0xEB8E,0xEAD6,0xE3AF --format lvgl -o ../source-fonts/material_symbols_shared_24.c --force-fast-kern-format ******************************************************************************/ #ifdef LV_LVGL_H_INCLUDE_SIMPLE @@ -127,6 +127,21 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { 0xf, 0x0, 0x0, 0x0, 0xf0, 0xf, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xbf, 0xfe, + /* U+E3AF "" */ + 0x0, 0x6, 0xff, 0x90, 0x0, 0x0, 0x7f, 0xff, + 0xfd, 0x0, 0x2, 0xfd, 0x3, 0x7f, 0x80, 0xb, + 0xd0, 0x9, 0x7, 0xe0, 0x1f, 0x80, 0x1c, 0x1, + 0xf4, 0x3e, 0xc0, 0x34, 0x0, 0x7c, 0x7c, 0x60, + 0x70, 0x0, 0x3d, 0xb4, 0x30, 0xff, 0xff, 0xfe, + 0xf0, 0x1f, 0xff, 0xc0, 0xf, 0xf0, 0xb, 0xff, + 0xc0, 0xf, 0xf0, 0x3, 0xff, 0xe0, 0xf, 0xf0, + 0x3, 0xff, 0xf4, 0xf, 0xbf, 0xff, 0xff, 0xc, + 0x1e, 0x7c, 0x0, 0xd, 0x9, 0x3d, 0x3d, 0x0, + 0x1c, 0x3, 0xbc, 0x1f, 0x40, 0x34, 0x2, 0xf4, + 0xb, 0xd0, 0x60, 0x7, 0xe0, 0x2, 0xfd, 0xc0, + 0x7f, 0x80, 0x0, 0x7f, 0xff, 0xfd, 0x0, 0x0, + 0x6, 0xff, 0x90, 0x0, + /* U+E3F4 "" */ 0x7f, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, @@ -387,6 +402,31 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { 0xff, 0x80, 0x0, 0x7f, 0xff, 0xfd, 0x0, 0x0, 0x6, 0xff, 0x90, 0x0, + /* U+EAD6 "" */ + 0x7f, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, + 0xff, 0xf0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, + 0x0, 0xf, 0xf0, 0x0, 0x0, 0x0, 0xff, 0x0, + 0x0, 0x0, 0xf, 0xf3, 0xf3, 0xf3, 0xc, 0xff, + 0x32, 0x74, 0x35, 0xcf, 0xf3, 0x27, 0x82, 0xa8, + 0xff, 0x32, 0x78, 0x1f, 0x4f, 0xf3, 0x27, 0x40, + 0xf0, 0xff, 0x3f, 0x3f, 0xa, 0xf, 0xf0, 0x0, + 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0xf, 0xf0, + 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0xf, + 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, + 0xfd, + + /* U+EB8E "" */ + 0x7f, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0, 0x0, 0x0, + 0xf, 0xf0, 0x20, 0x0, 0x0, 0xf, 0xf0, 0x7c, + 0x0, 0x0, 0xf, 0xf0, 0x3f, 0x0, 0x0, 0xf, + 0xf0, 0xf, 0xc0, 0x0, 0xf, 0xf0, 0xf, 0xc0, + 0x0, 0xf, 0xf0, 0x3f, 0x0, 0x0, 0xf, 0xf0, + 0x7c, 0xf, 0xff, 0xf, 0xf0, 0x20, 0xf, 0xff, + 0xf, 0xf0, 0x0, 0x0, 0x0, 0xf, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xfd, + /* U+EB97 "" */ 0x7f, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0, 0x0, 0x0, 0xf, 0xf0, @@ -561,40 +601,43 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { {.bitmap_index = 465, .adv_w = 384, .box_w = 20, .box_h = 20, .ofs_x = 2, .ofs_y = 2}, {.bitmap_index = 565, .adv_w = 384, .box_w = 12, .box_h = 7, .ofs_x = 6, .ofs_y = 9}, {.bitmap_index = 586, .adv_w = 384, .box_w = 20, .box_h = 16, .ofs_x = 2, .ofs_y = 4}, - {.bitmap_index = 666, .adv_w = 384, .box_w = 18, .box_h = 18, .ofs_x = 3, .ofs_y = 3}, - {.bitmap_index = 747, .adv_w = 384, .box_w = 12, .box_h = 18, .ofs_x = 6, .ofs_y = 3}, - {.bitmap_index = 801, .adv_w = 384, .box_w = 16, .box_h = 18, .ofs_x = 4, .ofs_y = 3}, - {.bitmap_index = 873, .adv_w = 384, .box_w = 16, .box_h = 16, .ofs_x = 4, .ofs_y = 4}, - {.bitmap_index = 937, .adv_w = 384, .box_w = 14, .box_h = 14, .ofs_x = 5, .ofs_y = 5}, - {.bitmap_index = 986, .adv_w = 384, .box_w = 18, .box_h = 12, .ofs_x = 3, .ofs_y = 6}, - {.bitmap_index = 1040, .adv_w = 384, .box_w = 4, .box_h = 16, .ofs_x = 10, .ofs_y = 4}, - {.bitmap_index = 1056, .adv_w = 384, .box_w = 16, .box_h = 16, .ofs_x = 4, .ofs_y = 4}, - {.bitmap_index = 1120, .adv_w = 384, .box_w = 24, .box_h = 17, .ofs_x = 0, .ofs_y = 3}, - {.bitmap_index = 1222, .adv_w = 384, .box_w = 16, .box_h = 12, .ofs_x = 4, .ofs_y = 6}, - {.bitmap_index = 1270, .adv_w = 384, .box_w = 18, .box_h = 16, .ofs_x = 4, .ofs_y = 4}, - {.bitmap_index = 1342, .adv_w = 384, .box_w = 18, .box_h = 17, .ofs_x = 3, .ofs_y = 4}, - {.bitmap_index = 1419, .adv_w = 384, .box_w = 16, .box_h = 20, .ofs_x = 4, .ofs_y = 2}, - {.bitmap_index = 1499, .adv_w = 384, .box_w = 20, .box_h = 19, .ofs_x = 2, .ofs_y = 3}, - {.bitmap_index = 1594, .adv_w = 384, .box_w = 20, .box_h = 20, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 666, .adv_w = 384, .box_w = 20, .box_h = 20, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 766, .adv_w = 384, .box_w = 18, .box_h = 18, .ofs_x = 3, .ofs_y = 3}, + {.bitmap_index = 847, .adv_w = 384, .box_w = 12, .box_h = 18, .ofs_x = 6, .ofs_y = 3}, + {.bitmap_index = 901, .adv_w = 384, .box_w = 16, .box_h = 18, .ofs_x = 4, .ofs_y = 3}, + {.bitmap_index = 973, .adv_w = 384, .box_w = 16, .box_h = 16, .ofs_x = 4, .ofs_y = 4}, + {.bitmap_index = 1037, .adv_w = 384, .box_w = 14, .box_h = 14, .ofs_x = 5, .ofs_y = 5}, + {.bitmap_index = 1086, .adv_w = 384, .box_w = 18, .box_h = 12, .ofs_x = 3, .ofs_y = 6}, + {.bitmap_index = 1140, .adv_w = 384, .box_w = 4, .box_h = 16, .ofs_x = 10, .ofs_y = 4}, + {.bitmap_index = 1156, .adv_w = 384, .box_w = 16, .box_h = 16, .ofs_x = 4, .ofs_y = 4}, + {.bitmap_index = 1220, .adv_w = 384, .box_w = 24, .box_h = 17, .ofs_x = 0, .ofs_y = 3}, + {.bitmap_index = 1322, .adv_w = 384, .box_w = 16, .box_h = 12, .ofs_x = 4, .ofs_y = 6}, + {.bitmap_index = 1370, .adv_w = 384, .box_w = 18, .box_h = 16, .ofs_x = 4, .ofs_y = 4}, + {.bitmap_index = 1442, .adv_w = 384, .box_w = 18, .box_h = 17, .ofs_x = 3, .ofs_y = 4}, + {.bitmap_index = 1519, .adv_w = 384, .box_w = 16, .box_h = 20, .ofs_x = 4, .ofs_y = 2}, + {.bitmap_index = 1599, .adv_w = 384, .box_w = 20, .box_h = 19, .ofs_x = 2, .ofs_y = 3}, {.bitmap_index = 1694, .adv_w = 384, .box_w = 20, .box_h = 20, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 1794, .adv_w = 384, .box_w = 16, .box_h = 20, .ofs_x = 4, .ofs_y = 2}, - {.bitmap_index = 1874, .adv_w = 384, .box_w = 16, .box_h = 18, .ofs_x = 4, .ofs_y = 3}, - {.bitmap_index = 1946, .adv_w = 384, .box_w = 20, .box_h = 16, .ofs_x = 2, .ofs_y = 4}, - {.bitmap_index = 2026, .adv_w = 384, .box_w = 24, .box_h = 23, .ofs_x = 0, .ofs_y = 1}, - {.bitmap_index = 2164, .adv_w = 384, .box_w = 18, .box_h = 18, .ofs_x = 3, .ofs_y = 3}, - {.bitmap_index = 2245, .adv_w = 384, .box_w = 20, .box_h = 20, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 2345, .adv_w = 384, .box_w = 20, .box_h = 18, .ofs_x = 2, .ofs_y = 3}, - {.bitmap_index = 2435, .adv_w = 384, .box_w = 18, .box_h = 20, .ofs_x = 3, .ofs_y = 2}, - {.bitmap_index = 2525, .adv_w = 384, .box_w = 16, .box_h = 20, .ofs_x = 4, .ofs_y = 2}, - {.bitmap_index = 2605, .adv_w = 384, .box_w = 18, .box_h = 16, .ofs_x = 4, .ofs_y = 4}, - {.bitmap_index = 2677, .adv_w = 384, .box_w = 20, .box_h = 20, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 2777, .adv_w = 384, .box_w = 18, .box_h = 18, .ofs_x = 3, .ofs_y = 3}, - {.bitmap_index = 2858, .adv_w = 384, .box_w = 18, .box_h = 18, .ofs_x = 3, .ofs_y = 3}, - {.bitmap_index = 2939, .adv_w = 384, .box_w = 22, .box_h = 16, .ofs_x = 1, .ofs_y = 4}, - {.bitmap_index = 3027, .adv_w = 384, .box_w = 16, .box_h = 16, .ofs_x = 4, .ofs_y = 4}, - {.bitmap_index = 3091, .adv_w = 384, .box_w = 22, .box_h = 16, .ofs_x = 1, .ofs_y = 4}, - {.bitmap_index = 3179, .adv_w = 384, .box_w = 18, .box_h = 20, .ofs_x = 3, .ofs_y = 2}, - {.bitmap_index = 3269, .adv_w = 384, .box_w = 20, .box_h = 20, .ofs_x = 2, .ofs_y = 2} + {.bitmap_index = 1794, .adv_w = 384, .box_w = 20, .box_h = 20, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 1894, .adv_w = 384, .box_w = 16, .box_h = 20, .ofs_x = 4, .ofs_y = 2}, + {.bitmap_index = 1974, .adv_w = 384, .box_w = 16, .box_h = 18, .ofs_x = 4, .ofs_y = 3}, + {.bitmap_index = 2046, .adv_w = 384, .box_w = 20, .box_h = 16, .ofs_x = 2, .ofs_y = 4}, + {.bitmap_index = 2126, .adv_w = 384, .box_w = 24, .box_h = 23, .ofs_x = 0, .ofs_y = 1}, + {.bitmap_index = 2264, .adv_w = 384, .box_w = 18, .box_h = 18, .ofs_x = 3, .ofs_y = 3}, + {.bitmap_index = 2345, .adv_w = 384, .box_w = 20, .box_h = 20, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 2445, .adv_w = 384, .box_w = 18, .box_h = 18, .ofs_x = 3, .ofs_y = 3}, + {.bitmap_index = 2526, .adv_w = 384, .box_w = 20, .box_h = 16, .ofs_x = 2, .ofs_y = 4}, + {.bitmap_index = 2606, .adv_w = 384, .box_w = 20, .box_h = 18, .ofs_x = 2, .ofs_y = 3}, + {.bitmap_index = 2696, .adv_w = 384, .box_w = 18, .box_h = 20, .ofs_x = 3, .ofs_y = 2}, + {.bitmap_index = 2786, .adv_w = 384, .box_w = 16, .box_h = 20, .ofs_x = 4, .ofs_y = 2}, + {.bitmap_index = 2866, .adv_w = 384, .box_w = 18, .box_h = 16, .ofs_x = 4, .ofs_y = 4}, + {.bitmap_index = 2938, .adv_w = 384, .box_w = 20, .box_h = 20, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 3038, .adv_w = 384, .box_w = 18, .box_h = 18, .ofs_x = 3, .ofs_y = 3}, + {.bitmap_index = 3119, .adv_w = 384, .box_w = 18, .box_h = 18, .ofs_x = 3, .ofs_y = 3}, + {.bitmap_index = 3200, .adv_w = 384, .box_w = 22, .box_h = 16, .ofs_x = 1, .ofs_y = 4}, + {.bitmap_index = 3288, .adv_w = 384, .box_w = 16, .box_h = 16, .ofs_x = 4, .ofs_y = 4}, + {.bitmap_index = 3352, .adv_w = 384, .box_w = 22, .box_h = 16, .ofs_x = 1, .ofs_y = 4}, + {.bitmap_index = 3440, .adv_w = 384, .box_w = 18, .box_h = 20, .ofs_x = 3, .ofs_y = 2}, + {.bitmap_index = 3530, .adv_w = 384, .box_w = 20, .box_h = 20, .ofs_x = 2, .ofs_y = 2} }; /*--------------------- @@ -603,11 +646,11 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { static const uint16_t unicode_list_0[] = { 0x0, 0x14, 0x62, 0x9b, 0x148, 0x182, 0x1ca, 0x1d1, - 0x1e1, 0x2af, 0x2c0, 0x418, 0x47e, 0x488, 0x48d, 0x48f, - 0x490, 0x4f9, 0x523, 0x600, 0x62b, 0x757, 0x76a, 0x773, - 0x7b8, 0x7ca, 0x7e9, 0x874, 0x8af, 0x8b2, 0x8c2, 0xa52, - 0xa87, 0xad7, 0xdfb, 0xe05, 0xe35, 0xea1, 0xee3, 0xf4b, - 0x1017, 0x15db, 0x1782 + 0x1e1, 0x26a, 0x2af, 0x2c0, 0x418, 0x47e, 0x488, 0x48d, + 0x48f, 0x490, 0x4f9, 0x523, 0x600, 0x62b, 0x757, 0x76a, + 0x773, 0x7b8, 0x7ca, 0x7e9, 0x874, 0x8af, 0x8b2, 0x8c2, + 0x991, 0xa49, 0xa52, 0xa87, 0xad7, 0xdfb, 0xe05, 0xe35, + 0xea1, 0xee3, 0xf4b, 0x1017, 0x15db, 0x1782 }; /*Collect the unicode lists and glyph_id offsets*/ @@ -615,7 +658,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] = { { .range_start = 57669, .range_length = 6019, .glyph_id_start = 1, - .unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 43, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + .unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 46, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY } }; diff --git a/Modules/lvgl-module/source-fonts/material_symbols_shared_32.c b/Modules/lvgl-module/source-fonts/material_symbols_shared_32.c index 903797867..90fd2664b 100644 --- a/Modules/lvgl-module/source-fonts/material_symbols_shared_32.c +++ b/Modules/lvgl-module/source-fonts/material_symbols_shared_32.c @@ -1,7 +1,7 @@ /******************************************************************************* * Size: 32 px * Bpp: 2 - * Opts: --no-compress --no-prefilter --bpp 2 --size 32 --font MaterialSymbolsRounded.ttf -r 0xE145,0xE5C3,0xE770,0xEF40,0xEBCC,0xEFE6,0xEF4A,0xE5CD,0xF15C,0xE668,0xE92E,0xE326,0xEB97,0xE745,0xEC1C,0xE2C7,0xF720,0xF090,0xE8AF,0xE30F,0xE8FD,0xE9F4,0xE3F4,0xE316,0xE90F,0xEA07,0xE9B9,0xE159,0xE5D2,0xE28D,0xE5D4,0xE405,0xE89C,0xF8C7,0xE5D5,0xEF7A,0xE8B8,0xE9F7,0xE55D,0xF028,0xE1E0,0xE63E,0xE1A7 --format lvgl -o ../source-fonts/material_symbols_shared_32.c --force-fast-kern-format + * Opts: --no-compress --no-prefilter --bpp 2 --size 32 --font MaterialSymbolsRounded.ttf -r 0xE145,0xE5C3,0xE770,0xEF40,0xEBCC,0xEFE6,0xEF4A,0xE5CD,0xF15C,0xE668,0xE92E,0xE326,0xEB97,0xE745,0xEC1C,0xE2C7,0xF720,0xF090,0xE8AF,0xE30F,0xE8FD,0xE9F4,0xE3F4,0xE316,0xE90F,0xEA07,0xE9B9,0xE159,0xE5D2,0xE28D,0xE5D4,0xE405,0xE89C,0xF8C7,0xE5D5,0xEF7A,0xE8B8,0xE9F7,0xE55D,0xF028,0xE1E0,0xE63E,0xE1A7,0xEB8E,0xEAD6,0xE3AF --format lvgl -o ../source-fonts/material_symbols_shared_32.c --force-fast-kern-format ******************************************************************************/ #ifdef LV_LVGL_H_INCLUDE_SIMPLE @@ -203,6 +203,33 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { 0x3f, 0xff, 0xfd, 0x2a, 0xaa, 0xaa, 0x90, 0xa, 0xaa, 0xa4, + /* U+E3AF "" */ + 0x0, 0x0, 0x1, 0x55, 0x40, 0x0, 0x0, 0x0, + 0x0, 0x2f, 0xff, 0xfd, 0x0, 0x0, 0x0, 0x2, + 0xff, 0xff, 0xff, 0xc0, 0x0, 0x0, 0x1f, 0xfe, + 0x42, 0xef, 0xf4, 0x0, 0x0, 0x7f, 0xd0, 0x3, + 0x42, 0xfe, 0x0, 0x1, 0xfe, 0x0, 0xb, 0x0, + 0x7f, 0x40, 0x3, 0xfc, 0x0, 0x1d, 0x0, 0x1f, + 0xc0, 0xb, 0xfd, 0x0, 0x38, 0x0, 0x7, 0xe0, + 0xf, 0xcb, 0x0, 0x70, 0x0, 0x3, 0xf4, 0x2f, + 0x43, 0x40, 0xf0, 0x0, 0x1, 0xf8, 0x3f, 0x2, + 0xc2, 0xff, 0xff, 0xff, 0xfc, 0x3e, 0x0, 0xe3, + 0xff, 0xf5, 0x55, 0xbc, 0x3e, 0x0, 0x7f, 0xff, + 0xf0, 0x0, 0x7d, 0x3d, 0x0, 0x3f, 0xff, 0xf4, + 0x0, 0x7d, 0x3d, 0x0, 0xf, 0xff, 0xfc, 0x0, + 0x7d, 0x3e, 0x0, 0xb, 0xff, 0xfe, 0x0, 0x7d, + 0x3f, 0x55, 0x5b, 0xff, 0xd7, 0x0, 0xbc, 0x2f, + 0xff, 0xff, 0xff, 0x83, 0xc0, 0xfc, 0x1f, 0x40, + 0x0, 0xf, 0x0, 0xd1, 0xf8, 0xf, 0xc0, 0x0, + 0xe, 0x0, 0xb3, 0xf0, 0x7, 0xe0, 0x0, 0x2c, + 0x0, 0x3f, 0xe0, 0x2, 0xf8, 0x0, 0x34, 0x0, + 0x2f, 0xc0, 0x0, 0xfe, 0x0, 0xe0, 0x0, 0xbf, + 0x0, 0x0, 0x3f, 0xd1, 0xc0, 0x7, 0xfc, 0x0, + 0x0, 0xb, 0xff, 0xea, 0xff, 0xf0, 0x0, 0x0, + 0x1, 0xff, 0xff, 0xff, 0x40, 0x0, 0x0, 0x0, + 0x1b, 0xff, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + /* U+E3F4 "" */ 0x2f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xaa, 0xaa, 0xaa, @@ -643,6 +670,48 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { 0x1b, 0xff, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + /* U+EAD6 "" */ + 0x2f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xbf, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xaa, 0xaa, 0xaa, + 0xaa, 0xbf, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x2f, + 0xf8, 0x0, 0x0, 0x0, 0x0, 0x2f, 0xf8, 0x0, + 0x0, 0x0, 0x0, 0x2f, 0xf8, 0x0, 0x0, 0x0, + 0x0, 0x2f, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x2f, + 0xf8, 0xff, 0x4f, 0xe7, 0x7, 0x2f, 0xf8, 0xe7, + 0xdd, 0x47, 0x4b, 0x2f, 0xf8, 0xd3, 0xdc, 0x3, + 0x8e, 0x2f, 0xf8, 0xd3, 0xdf, 0x82, 0xcd, 0x2f, + 0xf8, 0xd3, 0xde, 0x1, 0xec, 0x2f, 0xf8, 0xd3, + 0xdc, 0x0, 0xfc, 0x2f, 0xf8, 0xfb, 0xdf, 0x90, + 0xf8, 0x2f, 0xf8, 0xff, 0x4f, 0xe0, 0x70, 0x2f, + 0xf8, 0x0, 0x0, 0x0, 0x0, 0x2f, 0xf8, 0x0, + 0x0, 0x0, 0x0, 0x2f, 0xf8, 0x0, 0x0, 0x0, + 0x0, 0x2f, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x2f, + 0xf8, 0x0, 0x0, 0x0, 0x0, 0x2f, 0xfe, 0xaa, + 0xaa, 0xaa, 0xaa, 0xbf, 0xbf, 0xff, 0xff, 0xff, + 0xff, 0xfe, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xf8, + + /* U+EB8E "" */ + 0x1, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 0x1f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfd, 0x3f, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfe, 0x3e, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3e, 0x3e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, + 0x3e, 0x3, 0xe0, 0x0, 0x0, 0x0, 0x3e, 0x3e, + 0x3, 0xf8, 0x0, 0x0, 0x0, 0x3e, 0x3e, 0x0, + 0xfe, 0x0, 0x0, 0x0, 0x3e, 0x3e, 0x0, 0x3f, + 0x80, 0x0, 0x0, 0x3e, 0x3e, 0x0, 0x1f, 0xc0, + 0x0, 0x0, 0x3e, 0x3e, 0x0, 0x7f, 0x40, 0x0, + 0x0, 0x3e, 0x3e, 0x1, 0xfd, 0x0, 0x0, 0x0, + 0x3e, 0x3e, 0x3, 0xf4, 0x7, 0xff, 0xf0, 0x3e, + 0x3e, 0x3, 0xd0, 0xb, 0xff, 0xf0, 0x3e, 0x3e, + 0x0, 0x0, 0x2, 0xaa, 0x90, 0x3e, 0x3e, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x3e, 0x3f, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfd, 0x2f, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xfc, 0x6, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xa0, + /* U+EB97 "" */ 0xb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x3f, 0xaa, @@ -930,40 +999,43 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { {.bitmap_index = 879, .adv_w = 512, .box_w = 28, .box_h = 28, .ofs_x = 2, .ofs_y = 2}, {.bitmap_index = 1075, .adv_w = 512, .box_w = 16, .box_h = 9, .ofs_x = 8, .ofs_y = 12}, {.bitmap_index = 1111, .adv_w = 512, .box_w = 28, .box_h = 22, .ofs_x = 2, .ofs_y = 5}, - {.bitmap_index = 1265, .adv_w = 512, .box_w = 24, .box_h = 24, .ofs_x = 4, .ofs_y = 4}, - {.bitmap_index = 1409, .adv_w = 512, .box_w = 16, .box_h = 24, .ofs_x = 8, .ofs_y = 4}, - {.bitmap_index = 1505, .adv_w = 512, .box_w = 22, .box_h = 24, .ofs_x = 5, .ofs_y = 4}, - {.bitmap_index = 1637, .adv_w = 512, .box_w = 22, .box_h = 22, .ofs_x = 5, .ofs_y = 5}, - {.bitmap_index = 1758, .adv_w = 512, .box_w = 18, .box_h = 18, .ofs_x = 7, .ofs_y = 7}, - {.bitmap_index = 1839, .adv_w = 512, .box_w = 24, .box_h = 16, .ofs_x = 4, .ofs_y = 8}, - {.bitmap_index = 1935, .adv_w = 512, .box_w = 6, .box_h = 22, .ofs_x = 13, .ofs_y = 5}, - {.bitmap_index = 1968, .adv_w = 512, .box_w = 22, .box_h = 22, .ofs_x = 5, .ofs_y = 5}, - {.bitmap_index = 2089, .adv_w = 512, .box_w = 32, .box_h = 23, .ofs_x = 0, .ofs_y = 4}, - {.bitmap_index = 2273, .adv_w = 512, .box_w = 22, .box_h = 16, .ofs_x = 5, .ofs_y = 8}, - {.bitmap_index = 2361, .adv_w = 512, .box_w = 25, .box_h = 22, .ofs_x = 5, .ofs_y = 5}, - {.bitmap_index = 2499, .adv_w = 512, .box_w = 24, .box_h = 22, .ofs_x = 4, .ofs_y = 5}, - {.bitmap_index = 2631, .adv_w = 512, .box_w = 22, .box_h = 28, .ofs_x = 5, .ofs_y = 2}, - {.bitmap_index = 2785, .adv_w = 512, .box_w = 28, .box_h = 26, .ofs_x = 2, .ofs_y = 4}, - {.bitmap_index = 2967, .adv_w = 512, .box_w = 26, .box_h = 28, .ofs_x = 3, .ofs_y = 2}, - {.bitmap_index = 3149, .adv_w = 512, .box_w = 28, .box_h = 28, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 3345, .adv_w = 512, .box_w = 20, .box_h = 28, .ofs_x = 6, .ofs_y = 2}, - {.bitmap_index = 3485, .adv_w = 512, .box_w = 22, .box_h = 24, .ofs_x = 5, .ofs_y = 4}, - {.bitmap_index = 3617, .adv_w = 512, .box_w = 28, .box_h = 22, .ofs_x = 2, .ofs_y = 5}, - {.bitmap_index = 3771, .adv_w = 512, .box_w = 32, .box_h = 31, .ofs_x = 0, .ofs_y = 1}, - {.bitmap_index = 4019, .adv_w = 512, .box_w = 24, .box_h = 24, .ofs_x = 4, .ofs_y = 4}, - {.bitmap_index = 4163, .adv_w = 512, .box_w = 28, .box_h = 28, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 4359, .adv_w = 512, .box_w = 28, .box_h = 24, .ofs_x = 2, .ofs_y = 4}, - {.bitmap_index = 4527, .adv_w = 512, .box_w = 24, .box_h = 28, .ofs_x = 4, .ofs_y = 2}, - {.bitmap_index = 4695, .adv_w = 512, .box_w = 22, .box_h = 28, .ofs_x = 5, .ofs_y = 2}, - {.bitmap_index = 4849, .adv_w = 512, .box_w = 25, .box_h = 22, .ofs_x = 5, .ofs_y = 5}, - {.bitmap_index = 4987, .adv_w = 512, .box_w = 28, .box_h = 28, .ofs_x = 2, .ofs_y = 2}, - {.bitmap_index = 5183, .adv_w = 512, .box_w = 24, .box_h = 24, .ofs_x = 4, .ofs_y = 4}, - {.bitmap_index = 5327, .adv_w = 512, .box_w = 24, .box_h = 24, .ofs_x = 4, .ofs_y = 4}, - {.bitmap_index = 5471, .adv_w = 512, .box_w = 30, .box_h = 22, .ofs_x = 1, .ofs_y = 5}, - {.bitmap_index = 5636, .adv_w = 512, .box_w = 22, .box_h = 22, .ofs_x = 5, .ofs_y = 5}, - {.bitmap_index = 5757, .adv_w = 512, .box_w = 30, .box_h = 22, .ofs_x = 1, .ofs_y = 5}, - {.bitmap_index = 5922, .adv_w = 512, .box_w = 25, .box_h = 28, .ofs_x = 4, .ofs_y = 2}, - {.bitmap_index = 6097, .adv_w = 512, .box_w = 28, .box_h = 28, .ofs_x = 2, .ofs_y = 2} + {.bitmap_index = 1265, .adv_w = 512, .box_w = 28, .box_h = 28, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 1461, .adv_w = 512, .box_w = 24, .box_h = 24, .ofs_x = 4, .ofs_y = 4}, + {.bitmap_index = 1605, .adv_w = 512, .box_w = 16, .box_h = 24, .ofs_x = 8, .ofs_y = 4}, + {.bitmap_index = 1701, .adv_w = 512, .box_w = 22, .box_h = 24, .ofs_x = 5, .ofs_y = 4}, + {.bitmap_index = 1833, .adv_w = 512, .box_w = 22, .box_h = 22, .ofs_x = 5, .ofs_y = 5}, + {.bitmap_index = 1954, .adv_w = 512, .box_w = 18, .box_h = 18, .ofs_x = 7, .ofs_y = 7}, + {.bitmap_index = 2035, .adv_w = 512, .box_w = 24, .box_h = 16, .ofs_x = 4, .ofs_y = 8}, + {.bitmap_index = 2131, .adv_w = 512, .box_w = 6, .box_h = 22, .ofs_x = 13, .ofs_y = 5}, + {.bitmap_index = 2164, .adv_w = 512, .box_w = 22, .box_h = 22, .ofs_x = 5, .ofs_y = 5}, + {.bitmap_index = 2285, .adv_w = 512, .box_w = 32, .box_h = 23, .ofs_x = 0, .ofs_y = 4}, + {.bitmap_index = 2469, .adv_w = 512, .box_w = 22, .box_h = 16, .ofs_x = 5, .ofs_y = 8}, + {.bitmap_index = 2557, .adv_w = 512, .box_w = 25, .box_h = 22, .ofs_x = 5, .ofs_y = 5}, + {.bitmap_index = 2695, .adv_w = 512, .box_w = 24, .box_h = 22, .ofs_x = 4, .ofs_y = 5}, + {.bitmap_index = 2827, .adv_w = 512, .box_w = 22, .box_h = 28, .ofs_x = 5, .ofs_y = 2}, + {.bitmap_index = 2981, .adv_w = 512, .box_w = 28, .box_h = 26, .ofs_x = 2, .ofs_y = 4}, + {.bitmap_index = 3163, .adv_w = 512, .box_w = 26, .box_h = 28, .ofs_x = 3, .ofs_y = 2}, + {.bitmap_index = 3345, .adv_w = 512, .box_w = 28, .box_h = 28, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 3541, .adv_w = 512, .box_w = 20, .box_h = 28, .ofs_x = 6, .ofs_y = 2}, + {.bitmap_index = 3681, .adv_w = 512, .box_w = 22, .box_h = 24, .ofs_x = 5, .ofs_y = 4}, + {.bitmap_index = 3813, .adv_w = 512, .box_w = 28, .box_h = 22, .ofs_x = 2, .ofs_y = 5}, + {.bitmap_index = 3967, .adv_w = 512, .box_w = 32, .box_h = 31, .ofs_x = 0, .ofs_y = 1}, + {.bitmap_index = 4215, .adv_w = 512, .box_w = 24, .box_h = 24, .ofs_x = 4, .ofs_y = 4}, + {.bitmap_index = 4359, .adv_w = 512, .box_w = 28, .box_h = 28, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 4555, .adv_w = 512, .box_w = 24, .box_h = 24, .ofs_x = 4, .ofs_y = 4}, + {.bitmap_index = 4699, .adv_w = 512, .box_w = 28, .box_h = 22, .ofs_x = 2, .ofs_y = 5}, + {.bitmap_index = 4853, .adv_w = 512, .box_w = 28, .box_h = 24, .ofs_x = 2, .ofs_y = 4}, + {.bitmap_index = 5021, .adv_w = 512, .box_w = 24, .box_h = 28, .ofs_x = 4, .ofs_y = 2}, + {.bitmap_index = 5189, .adv_w = 512, .box_w = 22, .box_h = 28, .ofs_x = 5, .ofs_y = 2}, + {.bitmap_index = 5343, .adv_w = 512, .box_w = 25, .box_h = 22, .ofs_x = 5, .ofs_y = 5}, + {.bitmap_index = 5481, .adv_w = 512, .box_w = 28, .box_h = 28, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 5677, .adv_w = 512, .box_w = 24, .box_h = 24, .ofs_x = 4, .ofs_y = 4}, + {.bitmap_index = 5821, .adv_w = 512, .box_w = 24, .box_h = 24, .ofs_x = 4, .ofs_y = 4}, + {.bitmap_index = 5965, .adv_w = 512, .box_w = 30, .box_h = 22, .ofs_x = 1, .ofs_y = 5}, + {.bitmap_index = 6130, .adv_w = 512, .box_w = 22, .box_h = 22, .ofs_x = 5, .ofs_y = 5}, + {.bitmap_index = 6251, .adv_w = 512, .box_w = 30, .box_h = 22, .ofs_x = 1, .ofs_y = 5}, + {.bitmap_index = 6416, .adv_w = 512, .box_w = 25, .box_h = 28, .ofs_x = 4, .ofs_y = 2}, + {.bitmap_index = 6591, .adv_w = 512, .box_w = 28, .box_h = 28, .ofs_x = 2, .ofs_y = 2} }; /*--------------------- @@ -972,11 +1044,11 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { static const uint16_t unicode_list_0[] = { 0x0, 0x14, 0x62, 0x9b, 0x148, 0x182, 0x1ca, 0x1d1, - 0x1e1, 0x2af, 0x2c0, 0x418, 0x47e, 0x488, 0x48d, 0x48f, - 0x490, 0x4f9, 0x523, 0x600, 0x62b, 0x757, 0x76a, 0x773, - 0x7b8, 0x7ca, 0x7e9, 0x874, 0x8af, 0x8b2, 0x8c2, 0xa52, - 0xa87, 0xad7, 0xdfb, 0xe05, 0xe35, 0xea1, 0xee3, 0xf4b, - 0x1017, 0x15db, 0x1782 + 0x1e1, 0x26a, 0x2af, 0x2c0, 0x418, 0x47e, 0x488, 0x48d, + 0x48f, 0x490, 0x4f9, 0x523, 0x600, 0x62b, 0x757, 0x76a, + 0x773, 0x7b8, 0x7ca, 0x7e9, 0x874, 0x8af, 0x8b2, 0x8c2, + 0x991, 0xa49, 0xa52, 0xa87, 0xad7, 0xdfb, 0xe05, 0xe35, + 0xea1, 0xee3, 0xf4b, 0x1017, 0x15db, 0x1782 }; /*Collect the unicode lists and glyph_id offsets*/ @@ -984,7 +1056,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] = { { .range_start = 57669, .range_length = 6019, .glyph_id_start = 1, - .unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 43, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + .unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 46, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY } }; diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index e9dc85732..20192767e 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -82,6 +82,7 @@ #include #include #include +#include #include @@ -165,6 +166,7 @@ namespace app { namespace apppackagelist { extern const ::AppManifest manifest; } namespace audiosettings { extern const ::AppManifest manifest; } namespace boot { extern const ::AppManifest manifest; } + namespace camera { extern const ::AppManifest manifest; } namespace development { extern const ::AppManifest manifest; } namespace display { extern const ::AppManifest manifest; } namespace files { extern const ::AppManifest manifest; } @@ -224,6 +226,9 @@ static void registerInternalApps() { LOG_I(TAG, "Registering internal apps"); app_manager_add(&app::alertdialog::manifest); + if (device_exists_of_type(&CAMERA_TYPE)) { + app_manager_add(&app::camera::manifest); + } app_manager_add(&app::apppackagedetails::manifest); app_manager_add(&app::apphub::manifest); app_manager_add(&app::apphubdetails::manifest); diff --git a/Tactility/Source/app/applist/AppList.cpp b/Tactility/Source/app/applist/AppList.cpp index 073598ae9..3a12708b8 100644 --- a/Tactility/Source/app/applist/AppList.cpp +++ b/Tactility/Source/app/applist/AppList.cpp @@ -31,6 +31,30 @@ struct Context { Favourites favourites; }; +struct IconEntry { + const char* id; + const char* icon; +}; + +constexpr IconEntry ICONS[] = { + {"tactility.apphub", LVGL_ICON_SHARED_DOWNLOAD}, + {"tactility.camera", LVGL_ICON_SHARED_CAMERA}, + {"tactility.chat", LVGL_ICON_SHARED_FORUM}, + {"tactility.i2cscanner", LVGL_ICON_SHARED_CABLE}, + {"tactility.notes", LVGL_ICON_SHARED_EDIT_NOTE}, + {"tactility.screenshot", LVGL_ICON_SHARED_IMAGE}, + {"tactility.systeminfo", LVGL_ICON_SHARED_DEVICES}, + {"tactility.terminal", LVGL_ICON_SHARED_TERMINAL}, + {"tactility.webserversettings", LVGL_ICON_SHARED_CLOUD}, +}; + +const char* appIcon(const ::AppManifest* manifest) { + for (const auto& entry : ICONS) { + if (!strcmp(manifest->id, entry.id)) return entry.icon; + } + return LVGL_ICON_SHARED_DEPLOYED_CODE; +} + void populateList(lv_obj_t* list); // Named (not a lambda) so lv_async_call_cancel() below has a stable function pointer to match against. @@ -95,7 +119,7 @@ void onHelpPressed(lv_event_t* event) { lv_obj_t* createAppWidget(const ::AppManifest* manifest, lv_obj_t* list, bool favourite) { // Plain "*" prefix, not an icon: shared Material Symbols font is subsetted and has no star glyph. const std::string label = favourite ? (std::string("* ") + manifest->name) : manifest->name; - lv_obj_t* btn = lv_list_add_button(list, LVGL_ICON_SHARED_TOOLBAR, label.c_str()); + lv_obj_t* btn = lv_list_add_button(list, appIcon(manifest), label.c_str()); lv_obj_t* image = lv_obj_get_child(btn, 0); lv_obj_set_style_text_font(image, lvgl_get_shared_icon_font(), LV_PART_MAIN); // Not read by any event here (those get the manifest via their own callback user data) - lets diff --git a/Tactility/Source/app/apppackagelist/AppPackageList.cpp b/Tactility/Source/app/apppackagelist/AppPackageList.cpp index b69b21e9e..c9aafb10a 100644 --- a/Tactility/Source/app/apppackagelist/AppPackageList.cpp +++ b/Tactility/Source/app/apppackagelist/AppPackageList.cpp @@ -42,7 +42,7 @@ void onBackPressed(lv_event_t* event) { } void createPackageWidget(const char* packageId, lv_obj_t* list) { - lv_obj_t* btn = lv_list_add_button(list, LVGL_ICON_SHARED_TOOLBAR, packageId); + lv_obj_t* btn = lv_list_add_button(list, LVGL_ICON_SHARED_DEPLOYED_CODE, packageId); lv_obj_t* image = lv_obj_get_child(btn, 0); lv_obj_set_style_text_font(image, lvgl_get_shared_icon_font(), LV_PART_MAIN); lv_obj_add_event_cb(btn, &onPackagePressed, LV_EVENT_SHORT_CLICKED, const_cast(packageId)); diff --git a/Tactility/Source/app/camera/Camera.cpp b/Tactility/Source/app/camera/Camera.cpp new file mode 100644 index 000000000..d54e84986 --- /dev/null +++ b/Tactility/Source/app/camera/Camera.cpp @@ -0,0 +1,493 @@ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace tt::app::camera { + +extern const ::AppManifest manifest; + +constexpr uint32_t FRAME_TIMEOUT_MS = 200; + +namespace { + +struct Context { + uint32_t appInstanceId; + + CameraHandle camHandle = nullptr; + + uint8_t* displayBuf[2] = {}; + int displayBufIdx = 0; + int publishedDisplayBufIdx = -1; + uint32_t frameWidth = 0; + uint32_t frameHeight = 0; + + TaskHandle_t camTask = nullptr; + SemaphoreHandle_t stopSem = nullptr; + SemaphoreHandle_t doneSem = nullptr; + + SemaphoreHandle_t camHandleMutex = nullptr; + + std::atomic captureNext {false}; + std::atomic stopping {false}; + + lv_display_rotation_t displayRotation = LV_DISPLAY_ROTATION_0; + + lv_obj_t* previewContainer = nullptr; + lv_obj_t* previewCanvas = nullptr; + lv_obj_t* toastLabel = nullptr; + lv_timer_t* toastTimer = nullptr; +}; + +static void anim_opa_cb(void* var, int32_t v) { + lv_obj_set_style_opa((lv_obj_t*)var, v, 0); +} + +static void toast_fade_out_ready_cb(lv_anim_t* a) { + lv_obj_t* toast = (lv_obj_t*)a->var; + lv_obj_add_flag(toast, LV_OBJ_FLAG_HIDDEN); +} + +static void toastTimerCb(lv_timer_t *timer) { + auto* ctx = static_cast(lv_timer_get_user_data(timer)); + lv_timer_delete(timer); + if (ctx != nullptr) { + ctx->toastTimer = nullptr; + if (ctx->toastLabel != nullptr) { + lv_anim_t a; + lv_anim_init(&a); + lv_anim_set_var(&a, ctx->toastLabel); + lv_anim_set_values(&a, LV_OPA_90, LV_OPA_TRANSP); + lv_anim_set_time(&a, 300); + lv_anim_set_exec_cb(&a, anim_opa_cb); + lv_anim_set_completed_cb(&a, toast_fade_out_ready_cb); + lv_anim_start(&a); + } + } +} + +static void showToast(Context* ctx, const char *text) { + if (ctx == nullptr || ctx->toastLabel == nullptr) { + return; + } + if (!lvgl_try_lock(pdMS_TO_TICKS(100))) return; + + lv_anim_delete(ctx->toastLabel, anim_opa_cb); + + lv_label_set_text(ctx->toastLabel, text); + lv_obj_remove_flag(ctx->toastLabel, LV_OBJ_FLAG_HIDDEN); + lv_obj_move_foreground(ctx->toastLabel); + + lv_anim_t a; + lv_anim_init(&a); + lv_anim_set_var(&a, ctx->toastLabel); + lv_anim_set_values(&a, LV_OPA_TRANSP, LV_OPA_90); + lv_anim_set_time(&a, 250); + lv_anim_set_exec_cb(&a, anim_opa_cb); + lv_anim_set_path_cb(&a, lv_anim_path_ease_out); + lv_anim_start(&a); + + if (ctx->toastTimer != nullptr) { + lv_timer_del(ctx->toastTimer); + } + ctx->toastTimer = lv_timer_create(toastTimerCb, 3000, ctx); + lvgl_unlock(); +} + +void buildTimestampedPath(const char* suffix, char* out, size_t outLen) { + char dir[200]; + if (app_paths_get_user_data_directory("tactility.camera", dir, sizeof(dir)) != ERROR_NONE) { + LOG_E("Camera", "Failed to resolve user data directory"); + out[0] = '\0'; + return; + } + if (mkdir(dir, 0755) != 0 && errno != EEXIST) { + LOG_E("Camera", "mkdir failed for %s: errno=%d", dir, errno); + } + + time_t now = time(nullptr); + struct tm tm_info; + localtime_r(&now, &tm_info); + + static uint32_t sequence = 0; + char fname[64]; + strftime(fname, sizeof(fname), "/%Y%m%d_%H%M%S", &tm_info); + snprintf(out, outLen, "%s%s_%04u%s", dir, fname, static_cast(sequence++), suffix); +} + +bool saveJpeg(Context* ctx) { + if (!ctx->camHandle) { + showToast(ctx, "Capture failed: driver unavailable"); + return false; + } + + char path[256]; + buildTimestampedPath(".jpg", path, sizeof(path)); + + uint8_t* jpegBuf = nullptr; + size_t jpegLen = 0; + if (camera_capture_jpeg(ctx->camHandle, &jpegBuf, &jpegLen, 100) != ERROR_NONE || !jpegBuf) { + showToast(ctx, "Capture failed: encode error"); + return false; + } + + bool ok = false; + { + FILE* f = fopen(path, "wbx"); + if (f) { + const size_t written = fwrite(jpegBuf, 1, jpegLen, f); + const int closeResult = fclose(f); + ok = written == jpegLen && closeResult == 0; + if (!ok) { + unlink(path); + } + } else { + LOG_E("Camera", "fopen failed for %s: errno=%d", path, errno); + } + } + heap_caps_free(jpegBuf); + + if (ok) { + LOG_I("Camera", "Saved JPEG: %s (%zu bytes)", path, jpegLen); + const char* basename = strrchr(path, '/'); + char msg[sizeof(path) + 8]; + snprintf(msg, sizeof(msg), "Saved: %s", basename ? basename + 1 : path); + showToast(ctx, msg); + } else { + showToast(ctx, "Capture failed: cannot write file"); + } + return ok; +} + +static void syncRotation(Context* ctx) { + if (!ctx->camHandleMutex) return; + xSemaphoreTake(ctx->camHandleMutex, portMAX_DELAY); + + if (!ctx->camHandle) { + xSemaphoreGive(ctx->camHandleMutex); + return; + } + + CameraRotation rot; + switch (ctx->displayRotation) { + case LV_DISPLAY_ROTATION_90: rot = CAMERA_ROTATION_90; break; + case LV_DISPLAY_ROTATION_180: rot = CAMERA_ROTATION_180; break; + case LV_DISPLAY_ROTATION_270: rot = CAMERA_ROTATION_270; break; + default: rot = CAMERA_ROTATION_0; break; + } + camera_set_rotation(ctx->camHandle, rot); + + ctx->frameWidth = camera_get_width(ctx->camHandle); + ctx->frameHeight = camera_get_height(ctx->camHandle); + + xSemaphoreGive(ctx->camHandleMutex); +} + +void onDisplayRotation(lv_event_t* e) { + auto* ctx = static_cast(lv_event_get_user_data(e)); + if (!ctx) return; + // Called on LVGL task - safe to read display rotation here + lv_display_t* disp = static_cast(lv_event_get_target(e)); + if (disp) ctx->displayRotation = lv_display_get_rotation(disp); + syncRotation(ctx); +} + +void onCaptureBtn(lv_event_t* e) { + auto* ctx = static_cast(lv_event_get_user_data(e)); + if (ctx) { + ctx->captureNext.store(true); + showToast(ctx, "Capturing..."); + } +} + +void cameraTaskEntry(void* arg) { + auto* ctx = static_cast(arg); + + Device* camDev = nullptr; + if (device_get_first_active_by_type(&CAMERA_TYPE, &camDev) != ERROR_NONE) { + LOG_E("Camera", "Camera device not found"); + xSemaphoreGive(ctx->doneSem); + vTaskDelete(nullptr); + return; + } + + CameraHandle handle = nullptr; + if (camera_open(camDev, &handle) != ERROR_NONE) { + LOG_E("Camera", "camera_open failed"); + device_put(camDev); + xSemaphoreGive(ctx->doneSem); + vTaskDelete(nullptr); + return; + } + device_put(camDev); + + xSemaphoreTake(ctx->camHandleMutex, portMAX_DELAY); + ctx->camHandle = handle; + xSemaphoreGive(ctx->camHandleMutex); + syncRotation(ctx); + showToast(ctx, "Camera Started..."); + + size_t frameSz = (size_t)camera_get_width(handle) * camera_get_height(handle) * 2; + for (int i = 0; i < 2; i++) { + ctx->displayBuf[i] = static_cast(heap_caps_malloc(frameSz, MALLOC_CAP_SPIRAM)); + if (!ctx->displayBuf[i]) { + LOG_E("Camera", "Failed to alloc display buffer %d", i); + xSemaphoreTake(ctx->camHandleMutex, portMAX_DELAY); + camera_close(handle); + ctx->camHandle = nullptr; + xSemaphoreGive(ctx->camHandleMutex); + for (int j = 0; j < 2; j++) { + heap_caps_free(ctx->displayBuf[j]); + ctx->displayBuf[j] = nullptr; + } + xSemaphoreGive(ctx->doneSem); + vTaskDelete(nullptr); + return; + } + } + + uint8_t* frameBuf = nullptr; + size_t frameLen = 0; + + while (true) { + if (xSemaphoreTake(ctx->stopSem, 0) == pdTRUE) break; + + if (ctx->captureNext.load()) { + ctx->captureNext.store(false); + saveJpeg(ctx); + continue; + } + + uint32_t frameWidth = 0; + uint32_t frameHeight = 0; + error_t err = camera_get_frame(handle, &frameBuf, &frameLen, FRAME_TIMEOUT_MS, &frameWidth, &frameHeight); + if (err == ERROR_TIMEOUT) continue; + if (err != ERROR_NONE) { + LOG_E("Camera", "get_frame error: %d", (int)err); + break; + } + + int idx = ctx->publishedDisplayBufIdx < 0 ? 0 : ctx->publishedDisplayBufIdx ^ 1; + memcpy(ctx->displayBuf[idx], frameBuf, frameLen); + camera_release_frame(handle); + ctx->displayBufIdx = idx; + + if (lvgl_try_lock(pdMS_TO_TICKS(50))) { + lv_obj_t* previewCanvas = ctx->previewCanvas; + const bool stopping = ctx->stopping.load(); + auto w = static_cast(frameWidth); + auto h = static_cast(frameHeight); + if (previewCanvas != nullptr && !stopping) { + lv_canvas_set_buffer(previewCanvas, ctx->displayBuf[idx], w, h, LV_COLOR_FORMAT_RGB565); + ctx->publishedDisplayBufIdx = idx; + } + lvgl_unlock(); + } + } + + xSemaphoreTake(ctx->camHandleMutex, portMAX_DELAY); + camera_close(handle); + ctx->camHandle = nullptr; + xSemaphoreGive(ctx->camHandleMutex); + + for (int i = 0; i < 2; i++) { + heap_caps_free(ctx->displayBuf[i]); + ctx->displayBuf[i] = nullptr; + } + + xSemaphoreGive(ctx->doneSem); + vTaskDelete(nullptr); +} + +void startCamera(Context* ctx) { + ctx->stopSem = xSemaphoreCreateBinary(); + ctx->doneSem = xSemaphoreCreateBinary(); + ctx->camHandleMutex = xSemaphoreCreateMutex(); + + xTaskCreatePinnedToCore(cameraTaskEntry, "cam", 8 * 1024, ctx, 5, &ctx->camTask, 0); +} + +void stopCamera(Context* ctx) { + if (!ctx->camTask) return; + + xSemaphoreGive(ctx->stopSem); + while (xSemaphoreTake(ctx->doneSem, portMAX_DELAY) != pdTRUE) { + } + ctx->camTask = nullptr; + + vSemaphoreDelete(ctx->stopSem); ctx->stopSem = nullptr; + vSemaphoreDelete(ctx->doneSem); ctx->doneSem = nullptr; + vSemaphoreDelete(ctx->camHandleMutex); ctx->camHandleMutex = nullptr; +} + +void onBackPressed(lv_event_t* event) { + auto* ctx = static_cast(lv_event_get_user_data(event)); + app_event_emit_close(ctx->appInstanceId); +} + +void createWidgets(lv_obj_t* parent, void* userData) { + auto* ctx = static_cast(userData); + ctx->stopping.store(false); + + lv_obj_remove_flag(parent, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); + lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT); + + auto* toolbar = lvgl_toolbar_create(parent, "Camera"); + // The global toolbar nav callback only knows how to stop old-model apps. + lvgl_toolbar_set_nav_action(toolbar, LV_SYMBOL_CLOSE, onBackPressed, ctx); + lvgl_toolbar_add_text_button_action(toolbar, LV_SYMBOL_EYE_OPEN, onCaptureBtn, ctx); + + auto* main_wrapper = lv_obj_create(parent); + lv_obj_set_flex_flow(main_wrapper, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(main_wrapper, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); + lv_obj_set_flex_grow(main_wrapper, 1); + lv_obj_set_width(main_wrapper, LV_PCT(100)); + lv_obj_set_height(main_wrapper, LV_PCT(100)); + lv_obj_set_style_pad_all(main_wrapper, 6, 0); + lv_obj_set_style_border_width(main_wrapper, 0, 0); + lv_obj_remove_flag(main_wrapper, LV_OBJ_FLAG_SCROLLABLE); + + ctx->previewContainer = lv_obj_create(main_wrapper); + lv_obj_set_size(ctx->previewContainer, LV_PCT(100), LV_PCT(100)); + lv_obj_set_style_bg_color(ctx->previewContainer, lv_color_black(), 0); + lv_obj_set_style_border_width(ctx->previewContainer, 0, 0); + lv_obj_set_style_pad_all(ctx->previewContainer, 0, 0); + lv_obj_set_style_radius(ctx->previewContainer, 0, 0); + lv_obj_remove_flag(ctx->previewContainer, LV_OBJ_FLAG_SCROLLABLE); + + ctx->previewCanvas = lv_canvas_create(ctx->previewContainer); + lv_obj_set_style_bg_color(ctx->previewCanvas, lv_color_black(), 0); + lv_obj_set_style_bg_opa(ctx->previewCanvas, LV_OPA_COVER, 0); + lv_obj_center(ctx->previewCanvas); + + ctx->toastLabel = lv_label_create(lv_layer_top()); + lv_obj_align(ctx->toastLabel, LV_ALIGN_TOP_MID, 0, 52 * 2 + 24); + lv_obj_set_style_radius(ctx->toastLabel, 16, 0); + lv_obj_set_style_pad_hor(ctx->toastLabel, 24, 0); + lv_obj_set_style_pad_ver(ctx->toastLabel, 12, 0); + lv_obj_set_style_border_width(ctx->toastLabel, 2, 0); + lv_obj_set_style_text_font(ctx->toastLabel, lvgl_get_text_font(FONT_SIZE_DEFAULT), 0); + lv_obj_set_style_bg_opa(ctx->toastLabel, LV_OPA_90, 0); + lv_obj_set_style_bg_color(ctx->toastLabel, lv_color_hex(0x181825), 0); + lv_obj_set_style_text_color(ctx->toastLabel, lv_color_white(), 0); + lv_obj_set_style_border_color(ctx->toastLabel, lv_color_hex(0x3B82F6), 0); + lv_obj_set_style_shadow_width(ctx->toastLabel, 16, 0); + lv_obj_set_style_shadow_color(ctx->toastLabel, lv_color_black(), 0); + lv_obj_add_flag(ctx->toastLabel, LV_OBJ_FLAG_HIDDEN); + + lv_display_t* disp = lv_display_get_default(); + if (disp) { + ctx->displayRotation = lv_display_get_rotation(disp); + lv_display_add_event_cb(disp, onDisplayRotation, LV_EVENT_RESOLUTION_CHANGED, ctx); + } + + startCamera(ctx); +} + +void destroyWidgets(void* userData) { + auto* ctx = static_cast(userData); + + lv_display_t* disp = lv_display_get_default(); + if (disp) lv_display_remove_event_cb_with_user_data(disp, onDisplayRotation, ctx); + + if (ctx->toastTimer != nullptr) { + lv_timer_delete(ctx->toastTimer); + ctx->toastTimer = nullptr; + } + + if (ctx->toastLabel != nullptr) { + lv_anim_delete(ctx->toastLabel, nullptr); + } + + ctx->stopping.store(true); + + if (ctx->previewCanvas != nullptr) { + lv_obj_delete(ctx->previewCanvas); + } + ctx->previewCanvas = nullptr; + + stopCamera(ctx); + + ctx->previewContainer = nullptr; + if (ctx->toastLabel != nullptr) { + lv_obj_delete(ctx->toastLabel); + ctx->toastLabel = nullptr; + } +} + +int32_t appMain(int argc, char* argv[]) { + uint32_t appInstanceId = app_scheduler_current_app_id(); + Context ctx {}; + ctx.appInstanceId = appInstanceId; + + TaskEventGroup event_group {}; + task_event_group_construct(&event_group); + + AppEventSubscription sub {}; + check(app_event_subscribe(&sub, &event_group) == ERROR_NONE); + + WindowId window = window_manager_create_ext(appInstanceId, createWidgets, destroyWidgets, &ctx); + + bool shouldClose = false; + while (!shouldClose) { + task_event_group_wait_any(&event_group, nullptr, portMAX_DELAY); + + AppEvent event {}; + while (app_event_poll(&sub, &event) == ERROR_NONE) { + switch (event.type) { + case APP_EVENT_CLOSE: + shouldClose = true; + break; + default: + break; + } + if (shouldClose) break; + } + } + + window_manager_remove(window); + check(app_event_unsubscribe(&sub) == ERROR_NONE); + task_event_group_destruct(&event_group); + + return 0; +} + +} // namespace + +extern const ::AppManifest manifest = { + .id = "tactility.camera", + .name = "Camera", + .category = APP_CATEGORY_USER, + .location = { APP_LOCATION_MEMORY, reinterpret_cast(appMain) }, + .flags = 0, + .stack = {.depth = 4096, .desired_memory_capability = MEMORY_CAPABILITY_EXTERNAL } +}; + +} // namespace tt::app::camera diff --git a/Tactility/Source/app/settings/Settings.cpp b/Tactility/Source/app/settings/Settings.cpp index b027c4220..ec63c662c 100644 --- a/Tactility/Source/app/settings/Settings.cpp +++ b/Tactility/Source/app/settings/Settings.cpp @@ -25,6 +25,36 @@ struct Context { uint32_t appInstanceId; }; +struct IconEntry { + const char* id; + const char* icon; +}; + +constexpr IconEntry ICONS[] = { + {"tactility.apppackagelist", LVGL_ICON_SHARED_DEPLOYED_CODE}, + {"tactility.audiosettings", LVGL_ICON_SHARED_MUSIC_NOTE}, + {"tactility.btmanage", LVGL_ICON_SHARED_BLUETOOTH}, + {"tactility.development", LVGL_ICON_SHARED_LOGO_DEV}, + {"tactility.display", LVGL_ICON_SHARED_DISPLAY_SETTINGS}, + {"tactility.gpssettings", LVGL_ICON_SHARED_NAVIGATION}, + {"tactility.grovesettings", LVGL_ICON_SHARED_CABLE}, + {"tactility.keyboardsettings", LVGL_ICON_SHARED_KEYBOARD_ALT}, + {"tactility.localesettings", LVGL_ICON_SHARED_LANGUAGE}, + {"tactility.power", LVGL_ICON_SHARED_POWER_SETTINGS_NEW}, + {"tactility.timedatesettings", LVGL_ICON_SHARED_CALENDAR_MONTH}, + {"tactility.touchcalibration", LVGL_ICON_SHARED_CIRCLE}, + {"tactility.trackballsettings", LVGL_ICON_SHARED_DEVICES}, + {"tactility.usbsettings", LVGL_ICON_SHARED_USB}, + {"tactility.wifimanage", LVGL_ICON_SHARED_WIFI}, +}; + +const char* appIcon(const ::AppManifest* manifest) { + for (const auto& entry : ICONS) { + if (!strcmp(manifest->id, entry.id)) return entry.icon; + } + return LVGL_ICON_SHARED_TOOLBAR; +} + void onAppPressed(lv_event_t* e) { // Fire-and-forget top-level navigation, same as AppList's own app-launch buttons. const auto* manifest = static_cast(lv_event_get_user_data(e)); @@ -40,9 +70,7 @@ void onBackPressed(lv_event_t* event) { void createWidget(const ::AppManifest* manifest, lv_obj_t* list) { check(list); - // The new AppManifest has no per-app icon - use a shared generic one for every entry, - // same fallback the old model used for apps that didn't provide one. - auto* btn = lv_list_add_button(list, LVGL_ICON_SHARED_TOOLBAR, manifest->name); + auto* btn = lv_list_add_button(list, appIcon(manifest), manifest->name); lv_obj_t* image = lv_obj_get_child(btn, 0); lv_obj_set_style_text_font(image, lvgl_get_shared_icon_font(), LV_PART_MAIN); lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_SHORT_CLICKED, const_cast<::AppManifest*>(manifest)); diff --git a/Tactility/Source/service/statusbar/Statusbar.cpp b/Tactility/Source/service/statusbar/Statusbar.cpp index 40d9f069d..12215311a 100644 --- a/Tactility/Source/service/statusbar/Statusbar.cpp +++ b/Tactility/Source/service/statusbar/Statusbar.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -180,14 +181,19 @@ class StatusbarService final : public Service { Device* serial_dev = bluetooth_serial_get(); Device* midi_dev = bluetooth_midi_get(); + Device* hid_dev = bluetooth_hid_device_get(); bool connected = (serial_dev && bluetooth_serial_is_connected(serial_dev)) || - (midi_dev && bluetooth_midi_is_connected(midi_dev)); + (midi_dev && bluetooth_midi_is_connected(midi_dev)) || + (hid_dev && bluetooth_hid_device_is_connected(hid_dev)); if (serial_dev) { device_put(serial_dev); } if (midi_dev) { device_put(midi_dev); } + if (hid_dev) { + device_put(hid_dev); + } const char* desired_icon = getBluetoothStatusIcon(radio_state, scanning, connected); if (bt_last_icon != desired_icon) { if (desired_icon != nullptr) { diff --git a/Tactility/Source/settings/DisplaySettings.cpp b/Tactility/Source/settings/DisplaySettings.cpp index c515f39a0..bd992e485 100644 --- a/Tactility/Source/settings/DisplaySettings.cpp +++ b/Tactility/Source/settings/DisplaySettings.cpp @@ -228,14 +228,8 @@ bool save(const DisplaySettings& settings) { static bool isOriginallyLandscape() { auto* lvgl_display = lv_display_get_default(); - auto rotation = lv_display_get_rotation(lvgl_display); - // The lvgl resolution code compensates for rotation. We have to revert the compensation to get the real display resolution - // TODO: Use info from display driver - if (rotation == LV_DISPLAY_ROTATION_0 || rotation == LV_DISPLAY_ROTATION_180) { - return lv_display_get_physical_horizontal_resolution(lvgl_display) > lv_display_get_physical_vertical_resolution(lvgl_display); - } else { - return lv_display_get_physical_horizontal_resolution(lvgl_display) < lv_display_get_physical_vertical_resolution(lvgl_display); - } + return lv_display_get_original_horizontal_resolution(lvgl_display) > + lv_display_get_original_vertical_resolution(lvgl_display); } lv_display_rotation_t toLvglDisplayRotation(Orientation orientation) { diff --git a/Tactility/idf_component.yml b/Tactility/idf_component.yml index 984d41013..ea8299b45 100644 --- a/Tactility/idf_component.yml +++ b/Tactility/idf_component.yml @@ -1,11 +1,15 @@ dependencies: espressif/cjson: "^1.7.19" espressif/esp_codec_dev: "1.5.10" - espressif/esp_audio_codec: "2.4.0" + espressif/esp_audio_codec: "2.5.0" espressif/esp_video: version: "2.2.0" rules: - if: "target in [esp32s3, esp32c6, esp32p4]" + espressif/esp_new_jpeg: + version: "^1.0.2" + rules: + - if: "target in [esp32, esp32s3]" espressif/esp_hosted: version: "2.12.11" rules: From b2773d6665d8a087072ffa175b6b28e0f0898575 Mon Sep 17 00:00:00 2001 From: Shadowtrance Date: Sat, 26 Sep 2026 06:05:37 +1000 Subject: [PATCH 2/6] fixes --- Devices/m5stack-cores3/m5stack,cores3.dts | 1 - Devices/m5stack-stackchan/m5stack,stackchan.dts | 1 - Devices/m5stack-tab5/Source/devices/display_detect.cpp | 6 +++++- Drivers/gc0308-module/bindings/galaxycore,gc0308.yaml | 1 + Drivers/sc2356-module/source/sc2356.cpp | 2 +- Tactility/Source/Tactility.cpp | 4 ++++ Tactility/Source/app/camera/Camera.cpp | 3 +++ 7 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Devices/m5stack-cores3/m5stack,cores3.dts b/Devices/m5stack-cores3/m5stack,cores3.dts index fc3bbb053..221503afd 100644 --- a/Devices/m5stack-cores3/m5stack,cores3.dts +++ b/Devices/m5stack-cores3/m5stack,cores3.dts @@ -170,7 +170,6 @@ pin-vsync = <&gpio0 46 GPIO_FLAG_NONE>; pin-de = <&gpio0 38 GPIO_FLAG_NONE>; pin-pclk = <&gpio0 45 GPIO_FLAG_NONE>; - pin-xclk = <&gpio0 2 GPIO_FLAG_NONE>; pin-reset = <&aw9523b 8 GPIO_FLAG_ACTIVE_LOW>; }; }; diff --git a/Devices/m5stack-stackchan/m5stack,stackchan.dts b/Devices/m5stack-stackchan/m5stack,stackchan.dts index c7416fafd..8a823bcd0 100644 --- a/Devices/m5stack-stackchan/m5stack,stackchan.dts +++ b/Devices/m5stack-stackchan/m5stack,stackchan.dts @@ -183,7 +183,6 @@ pin-vsync = <&gpio0 46 GPIO_FLAG_NONE>; pin-de = <&gpio0 38 GPIO_FLAG_NONE>; pin-pclk = <&gpio0 45 GPIO_FLAG_NONE>; - pin-xclk = <&gpio0 2 GPIO_FLAG_NONE>; pin-reset = <&aw9523b 8 GPIO_FLAG_ACTIVE_LOW>; }; diff --git a/Devices/m5stack-tab5/Source/devices/display_detect.cpp b/Devices/m5stack-tab5/Source/devices/display_detect.cpp index d08167c85..fbbd1768f 100644 --- a/Devices/m5stack-tab5/Source/devices/display_detect.cpp +++ b/Devices/m5stack-tab5/Source/devices/display_detect.cpp @@ -56,7 +56,11 @@ bool pulse_display_reset_pins(Device* io_expander0) { gpio_descriptor_set_level(lcd_reset_pin, true); gpio_descriptor_set_level(touch_reset_pin, true); vTaskDelay(pdMS_TO_TICKS(10)); - gpio_descriptor_set_flags(lcd_reset_pin, GPIO_FLAG_DIRECTION_INPUT | GPIO_FLAG_PULL_UP); + if (gpio_descriptor_set_flags(lcd_reset_pin, GPIO_FLAG_DIRECTION_INPUT | GPIO_FLAG_PULL_UP) != ERROR_NONE) { + gpio_descriptor_release(lcd_reset_pin); + gpio_descriptor_release(touch_reset_pin); + return false; + } gpio_descriptor_set_level(touch_reset_pin, false); gpio_descriptor_release(lcd_reset_pin); diff --git a/Drivers/gc0308-module/bindings/galaxycore,gc0308.yaml b/Drivers/gc0308-module/bindings/galaxycore,gc0308.yaml index 98f7633ce..585e13883 100644 --- a/Drivers/gc0308-module/bindings/galaxycore,gc0308.yaml +++ b/Drivers/gc0308-module/bindings/galaxycore,gc0308.yaml @@ -38,6 +38,7 @@ properties: type: phandles pin-xclk: type: phandles + default: GPIO_PIN_SPEC_NONE pin-reset: type: phandles default: GPIO_PIN_SPEC_NONE diff --git a/Drivers/sc2356-module/source/sc2356.cpp b/Drivers/sc2356-module/source/sc2356.cpp index 4b959dbf0..2f7e9086c 100644 --- a/Drivers/sc2356-module/source/sc2356.cpp +++ b/Drivers/sc2356-module/source/sc2356.cpp @@ -174,7 +174,7 @@ error_t sc2356_open(Device* device, Sc2356Handle* out_handle) { return ERROR_OUT_OF_MEMORY; } - state->rotation = CAMERA_ROTATION_0; + state->rotation = add_rotation(CAMERA_ROTATION_0, GET_CONFIG(device)->rotation_offset); // Retrieve bus handle from the parent I2C controller auto* i2c = device_get_parent(device); diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 20192767e..e61d6ddfc 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -166,7 +166,9 @@ namespace app { namespace apppackagelist { extern const ::AppManifest manifest; } namespace audiosettings { extern const ::AppManifest manifest; } namespace boot { extern const ::AppManifest manifest; } +#ifdef ESP_PLATFORM namespace camera { extern const ::AppManifest manifest; } +#endif namespace development { extern const ::AppManifest manifest; } namespace display { extern const ::AppManifest manifest; } namespace files { extern const ::AppManifest manifest; } @@ -226,9 +228,11 @@ static void registerInternalApps() { LOG_I(TAG, "Registering internal apps"); app_manager_add(&app::alertdialog::manifest); +#ifdef ESP_PLATFORM if (device_exists_of_type(&CAMERA_TYPE)) { app_manager_add(&app::camera::manifest); } +#endif app_manager_add(&app::apppackagedetails::manifest); app_manager_add(&app::apphub::manifest); app_manager_add(&app::apphubdetails::manifest); diff --git a/Tactility/Source/app/camera/Camera.cpp b/Tactility/Source/app/camera/Camera.cpp index d54e84986..ac6679964 100644 --- a/Tactility/Source/app/camera/Camera.cpp +++ b/Tactility/Source/app/camera/Camera.cpp @@ -1,3 +1,4 @@ +#ifdef ESP_PLATFORM #include #include @@ -491,3 +492,5 @@ extern const ::AppManifest manifest = { }; } // namespace tt::app::camera + +#endif From 73f5e04aaaef3b7a1327dba59eb9d388df3c8e20 Mon Sep 17 00:00:00 2001 From: Shadowtrance Date: Sat, 26 Sep 2026 06:11:18 +1000 Subject: [PATCH 3/6] fix conflict --- Devices/m5stack-cores3/{devicetree.yaml => module.yaml} | 0 Devices/m5stack-stackchan/{devicetree.yaml => module.yaml} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Devices/m5stack-cores3/{devicetree.yaml => module.yaml} (100%) rename Devices/m5stack-stackchan/{devicetree.yaml => module.yaml} (100%) diff --git a/Devices/m5stack-cores3/devicetree.yaml b/Devices/m5stack-cores3/module.yaml similarity index 100% rename from Devices/m5stack-cores3/devicetree.yaml rename to Devices/m5stack-cores3/module.yaml diff --git a/Devices/m5stack-stackchan/devicetree.yaml b/Devices/m5stack-stackchan/module.yaml similarity index 100% rename from Devices/m5stack-stackchan/devicetree.yaml rename to Devices/m5stack-stackchan/module.yaml From 6dce7667061179b4a5682b82483b9bf5ed8695ee Mon Sep 17 00:00:00 2001 From: Shadowtrance Date: Sat, 26 Sep 2026 07:15:16 +1000 Subject: [PATCH 4/6] fixes --- Drivers/gc0308-module/include/drivers/gc0308.h | 2 +- .../gc0308-module/{devicetree.yaml => module.yaml} | 0 Drivers/gc0308-module/source/gc0308.cpp | 6 +++++- Tactility/Source/Tactility.cpp | 4 ---- Tactility/Source/app/camera/Camera.cpp | 12 +++++++----- Tactility/Source/app/settings/Settings.cpp | 2 +- 6 files changed, 14 insertions(+), 12 deletions(-) rename Drivers/gc0308-module/{devicetree.yaml => module.yaml} (100%) diff --git a/Drivers/gc0308-module/include/drivers/gc0308.h b/Drivers/gc0308-module/include/drivers/gc0308.h index 982a64aa7..f88bd04d0 100644 --- a/Drivers/gc0308-module/include/drivers/gc0308.h +++ b/Drivers/gc0308-module/include/drivers/gc0308.h @@ -110,7 +110,7 @@ error_t gc0308_set_rotation(Gc0308Handle handle, CameraRotation rotation); /** * Capture one frame and JPEG-encode it using the hardware JPEG encoder. - * Allocates output buffer in SPIRAM; caller must free it with heap_caps_free(). + * Allocates output buffer in SPIRAM; caller must free it with memory_free(). * @param handle handle returned by gc0308_open() * @param out_buf out: pointer to JPEG-encoded data (caller must free) * @param out_len out: byte length of JPEG data diff --git a/Drivers/gc0308-module/devicetree.yaml b/Drivers/gc0308-module/module.yaml similarity index 100% rename from Drivers/gc0308-module/devicetree.yaml rename to Drivers/gc0308-module/module.yaml diff --git a/Drivers/gc0308-module/source/gc0308.cpp b/Drivers/gc0308-module/source/gc0308.cpp index c74e1cb73..e7bf98ac6 100644 --- a/Drivers/gc0308-module/source/gc0308.cpp +++ b/Drivers/gc0308-module/source/gc0308.cpp @@ -55,6 +55,10 @@ struct Gc0308State : CameraHandleData { #define GET_CONFIG(device) (static_cast((device)->config)) +static gpio_num_t pin_or_nc(const GpioPinSpec& pin) { + return pin.gpio_controller == nullptr ? GPIO_NUM_NC : static_cast(pin.pin); +} + static CameraRotation add_rotation(CameraRotation rotation, uint16_t offset) { return static_cast((static_cast(rotation) + offset) % 360); } @@ -183,7 +187,7 @@ error_t gc0308_open(Device* device, Gc0308Handle* out_handle) { .vsync_io = static_cast(config->pin_vsync.pin), .de_io = static_cast(config->pin_de.pin), .pclk_io = static_cast(config->pin_pclk.pin), - .xclk_io = static_cast(config->pin_xclk.pin), + .xclk_io = pin_or_nc(config->pin_xclk), }, .xclk_freq = config->xclk_frequency_hz, }; diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 768642b56..b91798980 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -166,9 +166,7 @@ namespace app { namespace apppackagelist { extern const ::AppManifest manifest; } namespace audiosettings { extern const ::AppManifest manifest; } namespace boot { extern const ::AppManifest manifest; } -#ifdef ESP_PLATFORM namespace camera { extern const ::AppManifest manifest; } -#endif namespace development { extern const ::AppManifest manifest; } namespace display { extern const ::AppManifest manifest; } namespace files { extern const ::AppManifest manifest; } @@ -228,11 +226,9 @@ static void registerInternalApps() { LOG_I(TAG, "Registering internal apps"); app_manager_add(&app::alertdialog::manifest); -#ifdef ESP_PLATFORM if (device_exists_of_type(&CAMERA_TYPE)) { app_manager_add(&app::camera::manifest); } -#endif app_manager_add(&app::apppackagedetails::manifest); app_manager_add(&app::apphub::manifest); app_manager_add(&app::apphubdetails::manifest); diff --git a/Tactility/Source/app/camera/Camera.cpp b/Tactility/Source/app/camera/Camera.cpp index ac6679964..c8c5055f1 100644 --- a/Tactility/Source/app/camera/Camera.cpp +++ b/Tactility/Source/app/camera/Camera.cpp @@ -1,4 +1,3 @@ -#ifdef ESP_PLATFORM #include #include @@ -13,7 +12,6 @@ #include #include -#include #include #include @@ -256,7 +254,8 @@ void cameraTaskEntry(void* arg) { size_t frameSz = (size_t)camera_get_width(handle) * camera_get_height(handle) * 2; for (int i = 0; i < 2; i++) { - ctx->displayBuf[i] = static_cast(heap_caps_malloc(frameSz, MALLOC_CAP_SPIRAM)); + const MemoryPolicy policy { .required = 0, .desired = MEMORY_CAPABILITY_EXTERNAL, .alignment = 0 }; + ctx->displayBuf[i] = static_cast(memory_alloc_with_policy(frameSz, &policy)); if (!ctx->displayBuf[i]) { LOG_E("Camera", "Failed to alloc display buffer %d", i); xSemaphoreTake(ctx->camHandleMutex, portMAX_DELAY); @@ -295,6 +294,11 @@ void cameraTaskEntry(void* arg) { } int idx = ctx->publishedDisplayBufIdx < 0 ? 0 : ctx->publishedDisplayBufIdx ^ 1; + if (frameLen > frameSz || (size_t)frameWidth * frameHeight * 2 > frameSz) { + LOG_E("Camera", "Frame too large: %zu bytes (%ux%u)", frameLen, (unsigned)frameWidth, (unsigned)frameHeight); + camera_release_frame(handle); + continue; + } memcpy(ctx->displayBuf[idx], frameBuf, frameLen); camera_release_frame(handle); ctx->displayBufIdx = idx; @@ -492,5 +496,3 @@ extern const ::AppManifest manifest = { }; } // namespace tt::app::camera - -#endif diff --git a/Tactility/Source/app/settings/Settings.cpp b/Tactility/Source/app/settings/Settings.cpp index f4c6a1324..94ffc6ca4 100644 --- a/Tactility/Source/app/settings/Settings.cpp +++ b/Tactility/Source/app/settings/Settings.cpp @@ -56,7 +56,7 @@ const char* appIcon(const ::AppManifest* manifest) { for (const auto& entry : ICONS) { if (!strcmp(manifest->id, entry.id)) return entry.icon; } - return LVGL_ICON_SHARED_TOOLBAR; + return LVGL_ICON_SHARED_DEPLOYED_CODE; } void onAppPressed(lv_event_t* e) { From ea76f4a26f375887a63bb107026cc5e196591d88 Mon Sep 17 00:00:00 2001 From: Shadowtrance Date: Sat, 26 Sep 2026 07:22:25 +1000 Subject: [PATCH 5/6] Update Camera.cpp --- Tactility/Source/app/camera/Camera.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tactility/Source/app/camera/Camera.cpp b/Tactility/Source/app/camera/Camera.cpp index c8c5055f1..1782ec4d8 100644 --- a/Tactility/Source/app/camera/Camera.cpp +++ b/Tactility/Source/app/camera/Camera.cpp @@ -1,3 +1,4 @@ +#ifdef ESP_PLATFORM #include #include @@ -496,3 +497,5 @@ extern const ::AppManifest manifest = { }; } // namespace tt::app::camera + +#endif From d5f028b0ffdce5afdb9776803ca1757f415b357c Mon Sep 17 00:00:00 2001 From: Shadowtrance Date: Sat, 26 Sep 2026 07:33:36 +1000 Subject: [PATCH 6/6] Update Tactility.cpp --- Tactility/Source/Tactility.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index b91798980..c083b56fb 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -166,7 +166,6 @@ namespace app { namespace apppackagelist { extern const ::AppManifest manifest; } namespace audiosettings { extern const ::AppManifest manifest; } namespace boot { extern const ::AppManifest manifest; } - namespace camera { extern const ::AppManifest manifest; } namespace development { extern const ::AppManifest manifest; } namespace display { extern const ::AppManifest manifest; } namespace files { extern const ::AppManifest manifest; } @@ -202,6 +201,7 @@ namespace app { namespace webserversettings { extern const ::AppManifest manifest; } #ifdef ESP_PLATFORM namespace apwebserver { extern const ::AppManifest manifest; } + namespace camera { extern const ::AppManifest manifest; } namespace crashdiagnostics { extern const ::AppManifest manifest; } #if CONFIG_TT_TDECK_WORKAROUND == 1 namespace keyboardsettings { extern const ::AppManifest manifest; } // T-Deck only for now @@ -226,9 +226,6 @@ static void registerInternalApps() { LOG_I(TAG, "Registering internal apps"); app_manager_add(&app::alertdialog::manifest); - if (device_exists_of_type(&CAMERA_TYPE)) { - app_manager_add(&app::camera::manifest); - } app_manager_add(&app::apppackagedetails::manifest); app_manager_add(&app::apphub::manifest); app_manager_add(&app::apphubdetails::manifest); @@ -271,6 +268,9 @@ static void registerInternalApps() { app_manager_add(&app::webserversettings::manifest); #ifdef ESP_PLATFORM app_manager_add(&app::apwebserver::manifest); + if (device_exists_of_type(&CAMERA_TYPE)) { + app_manager_add(&app::camera::manifest); + } app_manager_add(&app::crashdiagnostics::manifest); #if defined(CONFIG_TT_TDECK_WORKAROUND) app_manager_add(&app::keyboardsettings::manifest);