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/m5stack,cores3.dts b/Devices/m5stack-cores3/m5stack,cores3.dts index aaefa217c..221503afd 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,25 @@ // 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-reset = <&aw9523b 8 GPIO_FLAG_ACTIVE_LOW>; + }; }; port_a: grove0 { diff --git a/Devices/m5stack-cores3/module.yaml b/Devices/m5stack-cores3/module.yaml index f20153421..239e1835f 100644 --- a/Devices/m5stack-cores3/module.yaml +++ b/Devices/m5stack-cores3/module.yaml @@ -5,6 +5,7 @@ dependencies: - Drivers/aw9523b-module - Drivers/axp2101-module - Drivers/bm8563-module +- Drivers/gc0308-module - Drivers/bmi270-module - Drivers/es7210-module - Drivers/ft5x06-module 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/m5stack,stackchan.dts b/Devices/m5stack-stackchan/m5stack,stackchan.dts index 706f2dfb0..8a823bcd0 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,26 @@ 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-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-stackchan/module.yaml b/Devices/m5stack-stackchan/module.yaml index 4e30a838d..c7bb0441c 100644 --- a/Devices/m5stack-stackchan/module.yaml +++ b/Devices/m5stack-stackchan/module.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-tab5/Source/devices/display_detect.cpp b/Devices/m5stack-tab5/Source/devices/display_detect.cpp index 2e88f9fcf..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_level(lcd_reset_pin, false); + 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/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..585e13883 --- /dev/null +++ b/Drivers/gc0308-module/bindings/galaxycore,gc0308.yaml @@ -0,0 +1,46 @@ +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 + default: GPIO_PIN_SPEC_NONE + 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/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..f88bd04d0 --- /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 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 + * @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/module.yaml b/Drivers/gc0308-module/module.yaml new file mode 100644 index 000000000..9424b9a3a --- /dev/null +++ b/Drivers/gc0308-module/module.yaml @@ -0,0 +1,3 @@ +dependencies: +- TactilityKernel +bindings: bindings diff --git a/Drivers/gc0308-module/source/gc0308.cpp b/Drivers/gc0308-module/source/gc0308.cpp new file mode 100644 index 000000000..e7bf98ac6 --- /dev/null +++ b/Drivers/gc0308-module/source/gc0308.cpp @@ -0,0 +1,552 @@ +// 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 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); +} + +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 = pin_or_nc(config->pin_xclk), + }, + .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..2f7e9086c 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. @@ -170,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); @@ -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 6bc57a3e6..c083b56fb 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -82,6 +82,7 @@ #include #include #include +#include #include @@ -200,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 @@ -266,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); diff --git a/Tactility/Source/app/applist/AppList.cpp b/Tactility/Source/app/applist/AppList.cpp index c9127999c..91e94defb 100644 --- a/Tactility/Source/app/applist/AppList.cpp +++ b/Tactility/Source/app/applist/AppList.cpp @@ -32,6 +32,30 @@ struct Context { std::vector<::AppManifest> manifests; }; +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. @@ -96,7 +120,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..1782ec4d8 --- /dev/null +++ b/Tactility/Source/app/camera/Camera.cpp @@ -0,0 +1,501 @@ +#ifdef ESP_PLATFORM +#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++) { + 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); + 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; + 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; + + 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 + +#endif diff --git a/Tactility/Source/app/settings/Settings.cpp b/Tactility/Source/app/settings/Settings.cpp index 2f8ce5dfa..94ffc6ca4 100644 --- a/Tactility/Source/app/settings/Settings.cpp +++ b/Tactility/Source/app/settings/Settings.cpp @@ -29,6 +29,36 @@ struct Context { std::vector<::AppManifest> manifests; }; +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_DEPLOYED_CODE; +} + 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)); @@ -49,9 +79,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: