Tab5, CoreS3, StackChan and app icons (#662)

This commit is contained in:
Shadowtrance 2026-09-26 08:35:33 +10:00 committed by GitHub
parent 85b61e1e43
commit 7c508bbaa0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 2208 additions and 238 deletions

View File

@ -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

View File

@ -20,6 +20,7 @@
#include <bindings/es7210.h>
#include <bindings/ft5x06.h>
#include <bindings/ili9341.h>
#include <bindings/gc0308.h>
// 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 {

View File

@ -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

View File

@ -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

View File

@ -19,10 +19,11 @@
#include <bindings/bm8563.h>
#include <bindings/bmi270.h>
#include <bindings/es7210.h>
#include <bindings/ft6x36.h>
#include <bindings/ft5x06.h>
#include <bindings/ili9341.h>
#include <bindings/ina226.h>
#include <bindings/py32ioexpander.h>
#include <bindings/gc0308.h>
// 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
};

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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;
};
/**

View File

@ -6,6 +6,8 @@
#include <tactility/drivers/i2c_controller.h>
#include <tactility/log.h>
#include <algorithm>
#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<int16_t>(static_cast<uint16_t>(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;
}

View File

@ -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
)

View File

@ -0,0 +1,195 @@
Apache License
==============
_Version 2.0, January 2004_
_&lt;<http://www.apache.org/licenses/>&gt;_
### 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.

View File

@ -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)

View File

@ -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.

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <tactility/bindings/bindings.h>
#include <drivers/gc0308.h>
#ifdef __cplusplus
extern "C" {
#endif
DEFINE_DEVICETREE(gc0308, struct Gc0308Config)
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,124 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <tactility/drivers/camera.h>
#include <tactility/drivers/gpio.h>
#include <tactility/error.h>
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

View File

@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <tactility/module.h>
#ifdef __cplusplus
extern "C" {
#endif
extern struct Module gc0308_module;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,3 @@
dependencies:
- TactilityKernel
bindings: bindings

View File

@ -0,0 +1,552 @@
// SPDX-License-Identifier: Apache-2.0
#include <drivers/gc0308.h>
#include <gc0308_module.h>
#include <tactility/device.h>
#include <tactility/drivers/esp32_i2c_master.h>
#include <tactility/drivers/gpio_controller.h>
#include <tactility/drivers/i2c_controller.h>
#include <tactility/log.h>
#include <esp_heap_caps.h>
#include <esp_video_device.h>
#include <esp_video_init.h>
#include <freertos/task.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <esp_jpeg_enc.h>
#include <cstring>
#include <freertos/semphr.h>
#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<const Gc0308Config*>((device)->config))
static gpio_num_t pin_or_nc(const GpioPinSpec& pin) {
return pin.gpio_controller == nullptr ? GPIO_NUM_NC : static_cast<gpio_num_t>(pin.pin);
}
static CameraRotation add_rotation(CameraRotation rotation, uint16_t offset) {
return static_cast<CameraRotation>((static_cast<uint16_t>(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<Gc0308State*>(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<gpio_num_t>(config->pin_d0.pin),
static_cast<gpio_num_t>(config->pin_d1.pin),
static_cast<gpio_num_t>(config->pin_d2.pin),
static_cast<gpio_num_t>(config->pin_d3.pin),
static_cast<gpio_num_t>(config->pin_d4.pin),
static_cast<gpio_num_t>(config->pin_d5.pin),
static_cast<gpio_num_t>(config->pin_d6.pin),
static_cast<gpio_num_t>(config->pin_d7.pin),
},
.vsync_io = static_cast<gpio_num_t>(config->pin_vsync.pin),
.de_io = static_cast<gpio_num_t>(config->pin_de.pin),
.pclk_io = static_cast<gpio_num_t>(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<uint8_t*>(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<uint8_t*>(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<int>(state->width),
.height = static_cast<int>(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<Gc0308State*>(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<Gc0308State*>(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<Gc0308State*>(handle);
struct pollfd poll_descriptor = { .fd = state->fd, .events = POLLIN };
int poll_result = poll(&poll_descriptor, 1, static_cast<int>(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<int>(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<Gc0308State*>(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<uint32_t>(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<Gc0308State*>(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<Gc0308State*>(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<Gc0308State*>(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<uint8_t*>(heap_caps_aligned_alloc(16, image_size, MALLOC_CAP_SPIRAM));
uint8_t* jpeg_output = static_cast<uint8_t*>(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<int>(frame_width);
cfg.height = static_cast<int>(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<int>(frame_len), jpeg_output, static_cast<int>(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<int>(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"

View File

@ -0,0 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
#include <tactility/driver.h>
#include <tactility/module.h>
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"

View File

@ -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

View File

@ -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;
};
/**

View File

@ -5,6 +5,8 @@
#include <tactility/drivers/i2c_controller.h>
#include <tactility/log.h>
#include <algorithm>
#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;
}

View File

@ -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

View File

@ -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;
};
/**

View File

@ -5,6 +5,8 @@
#include <tactility/drivers/i2c_controller.h>
#include <tactility/log.h>
#include <algorithm>
#define TAG "QMI8658"
// Register map (QMI8658 datasheet)
@ -107,9 +109,21 @@ error_t qmi8658_read_accel(Device* device, ImuAccelData* data) {
return static_cast<int16_t>(static_cast<uint16_t>(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;
}

View File

@ -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).

View File

@ -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.

View File

@ -64,6 +64,10 @@ struct Sc2356State : CameraHandleData {
#define GET_CONFIG(device) (static_cast<const Sc2356Config*>((device)->config))
static CameraRotation add_rotation(CameraRotation rotation, uint16_t offset) {
return static_cast<CameraRotation>((static_cast<uint16_t>(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<Sc2356State*>(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;

View File

@ -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:

View File

@ -111,6 +111,9 @@ shared_symbol_code_point_names = [
"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

View File

@ -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"

View File

@ -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
}
};

View File

@ -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
}
};

View File

@ -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
}
};

View File

@ -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
}
};

View File

@ -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
}
};

View File

@ -82,6 +82,7 @@
#include <tactility/concurrent/thread.h>
#include <tactility/device.h>
#include <tactility/drivers/audio_stream.h>
#include <tactility/drivers/camera.h>
#include <tactility/drivers/display.h>
@ -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);

View File

@ -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

View File

@ -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<char*>(packageId));

View File

@ -0,0 +1,501 @@
#ifdef ESP_PLATFORM
#include <Tactility/Tactility.h>
#include <app/event.h>
#include <app/manager.h>
#include <app/manifest.h>
#include <app/paths.h>
#include <app/scheduler.h>
#include <atomic>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <lvgl/fonts.h>
#include <lvgl/lvgl.h>
#include <lvgl/widgets/toolbar.h>
#include <lvgl_window_manager/window_manager.h>
#include <sys/stat.h>
#include <unistd.h>
#include <tactility/check.h>
#include <tactility/device.h>
#include <tactility/drivers/camera.h>
#include <tactility/memory.h>
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<bool> captureNext {false};
std::atomic<bool> 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<Context*>(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<unsigned>(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<Context*>(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_display_t*>(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<Context*>(lv_event_get_user_data(e));
if (ctx) {
ctx->captureNext.store(true);
showToast(ctx, "Capturing...");
}
}
void cameraTaskEntry(void* arg) {
auto* ctx = static_cast<Context*>(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<uint8_t*>(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<int32_t>(frameWidth);
auto h = static_cast<int32_t>(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<Context*>(lv_event_get_user_data(event));
app_event_emit_close(ctx->appInstanceId);
}
void createWidgets(lv_obj_t* parent, void* userData) {
auto* ctx = static_cast<Context*>(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<Context*>(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<void*>(appMain) },
.flags = 0,
.stack = {.depth = 4096, .desired_memory_capability = MEMORY_CAPABILITY_EXTERNAL }
};
} // namespace tt::app::camera
#endif

View File

@ -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<const ::AppManifest*>(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));

View File

@ -11,6 +11,7 @@
#include <tactility/check.h>
#include <tactility/device.h>
#include <tactility/drivers/bluetooth.h>
#include <tactility/drivers/bluetooth_hid_device.h>
#include <tactility/drivers/bluetooth_midi.h>
#include <tactility/drivers/bluetooth_serial.h>
#include <tactility/drivers/power_supply.h>
@ -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) {

View File

@ -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) {

View File

@ -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: