mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-08-20 17:05:06 +00:00
Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ff1627385 | ||
|
|
b03759a111 | ||
|
|
f943c4dd69 | ||
|
|
d6b1d15e56 | ||
|
|
cc8be3faef | ||
|
|
37c507544b | ||
|
|
dc3f6104b8 | ||
|
|
564d8af64c | ||
|
|
c6373b79e9 | ||
|
|
d2c69ee7e8 | ||
|
|
85fe1a319a | ||
|
|
d72d5a0ef2 | ||
|
|
94deee8875 | ||
|
|
f13c18f398 | ||
|
|
606b918f9c | ||
|
|
1cb661469d | ||
|
|
c729e8340f | ||
|
|
de6fc3b346 | ||
|
|
acb2f1d4c7 | ||
|
|
6e55e71e67 | ||
|
|
bd108cc3c4 | ||
|
|
e6e1dcd0ca | ||
|
|
58a529cc44 | ||
|
|
d1f06cb774 | ||
|
|
3354924359 | ||
|
|
03a6285328 | ||
|
|
f21c0df6fe | ||
|
|
b98a813f3c | ||
|
|
cd2d9d6158 | ||
|
|
d4ef83e316 | ||
|
|
db7468d0c8 | ||
|
|
ca5b071859 | ||
|
|
2a2558b29a | ||
|
|
29e80cfd65 | ||
|
|
429125734a | ||
|
|
08eac48e64 | ||
|
|
8b92aa8e5a | ||
|
|
a3fda9ad8f | ||
|
|
2fbc44466a | ||
|
|
2d768ef3a1 | ||
|
|
bd30aa046a | ||
|
|
f9453d8956 | ||
|
|
5f54f7ca3d | ||
|
|
d896657bf9 | ||
|
|
3b5a401594 | ||
|
|
6fb2bb736c | ||
|
|
bd8fdfd858 | ||
|
|
8af6204ba1 | ||
|
|
955416dac8 | ||
|
|
fa4a6e255c | ||
|
|
50c0a14a93 | ||
|
|
c4406b24ba | ||
|
|
0922c4d55b | ||
|
|
7a7f09be35 | ||
|
|
16a61a087c | ||
|
|
1c2806bddf | ||
|
|
dbb96a891c | ||
|
|
d6ac070e30 | ||
|
|
f4f91f81d6 | ||
|
|
cca7224252 | ||
|
|
66a4eb66d6 | ||
|
|
dfaeb9a2d9 | ||
|
|
9d5993930d | ||
|
|
ecad2248d9 | ||
|
|
a323f8e148 | ||
|
|
05720821f8 | ||
|
|
90afba647e | ||
|
|
35fd7dd536 |
65
.claude/rules/CLAUDE.md
Normal file
65
.claude/rules/CLAUDE.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
|
||||||
|
|
||||||
|
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
||||||
|
|
||||||
|
## 1. Think Before Coding
|
||||||
|
|
||||||
|
**Don't assume. Don't hide confusion. Surface tradeoffs.**
|
||||||
|
|
||||||
|
Before implementing:
|
||||||
|
- State your assumptions explicitly. If uncertain, ask.
|
||||||
|
- If multiple interpretations exist, present them - don't pick silently.
|
||||||
|
- If a simpler approach exists, say so. Push back when warranted.
|
||||||
|
- If something is unclear, stop. Name what's confusing. Ask.
|
||||||
|
|
||||||
|
## 2. Simplicity First
|
||||||
|
|
||||||
|
**Minimum code that solves the problem. Nothing speculative.**
|
||||||
|
|
||||||
|
- No features beyond what was asked.
|
||||||
|
- No abstractions for single-use code.
|
||||||
|
- No "flexibility" or "configurability" that wasn't requested.
|
||||||
|
- No error handling for impossible scenarios.
|
||||||
|
- If you write 200 lines and it could be 50, rewrite it.
|
||||||
|
|
||||||
|
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
|
||||||
|
|
||||||
|
## 3. Surgical Changes
|
||||||
|
|
||||||
|
**Touch only what you must. Clean up only your own mess.**
|
||||||
|
|
||||||
|
When editing existing code:
|
||||||
|
- Don't "improve" adjacent code, comments, or formatting.
|
||||||
|
- Don't refactor things that aren't broken.
|
||||||
|
- Match existing style, even if you'd do it differently.
|
||||||
|
- If you notice unrelated dead code, mention it - don't delete it.
|
||||||
|
|
||||||
|
When your changes create orphans:
|
||||||
|
- Remove imports/variables/functions that YOUR changes made unused.
|
||||||
|
- Don't remove pre-existing dead code unless asked.
|
||||||
|
|
||||||
|
The test: Every changed line should trace directly to the user's request.
|
||||||
|
|
||||||
|
## 4. Goal-Driven Execution
|
||||||
|
|
||||||
|
**Define success criteria. Loop until verified.**
|
||||||
|
|
||||||
|
Transform tasks into verifiable goals:
|
||||||
|
- "Add validation" → "Write tests for invalid inputs, then make them pass"
|
||||||
|
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
|
||||||
|
- "Refactor X" → "Ensure tests pass before and after"
|
||||||
|
|
||||||
|
For multi-step tasks, state a brief plan:
|
||||||
|
```
|
||||||
|
1. [Step] → verify: [check]
|
||||||
|
2. [Step] → verify: [check]
|
||||||
|
3. [Step] → verify: [check]
|
||||||
|
```
|
||||||
|
|
||||||
|
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
|
||||||
18
.claude/rules/app-framework.md
Normal file
18
.claude/rules/app-framework.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Architecture: App Framework
|
||||||
|
|
||||||
|
Apps are event-driven, C API (`app-module`, `<app/*.h>`), not a C++ class. Each app has an `AppManifest` (`id`, `name`, `category`, `location`, `flags`) and a `main(app_instance_id, argc, argv)` entry point (`AppMainFn`), modelled on a C program's `main()`. Every app instance gets its own dedicated task for its whole lifetime, and blocks in that task until it returns.
|
||||||
|
|
||||||
|
Lifecycle and inter-app communication go through `app_manager_*()` (`app/manager.h`) and `app_event_*()` (`app/event.h`):
|
||||||
|
- `app_manager_start()`/`app_manager_start_with_parameters()` launch a plain instance; `app_manager_start_for_result()` launches a modal child that reports back to a parent instance.
|
||||||
|
- An app subscribes with `app_event_subscribe()`/`app_event_await()` and reacts to `APP_EVENT_CLOSE` (terminate now) and `APP_EVENT_RESULT` (a child it started reported back).
|
||||||
|
- An app closes itself by calling `app_manager_finish()` right before returning from `main()`; another instance is closed via `app_manager_stop()`.
|
||||||
|
|
||||||
|
Apps are registered at startup via `app_manager_add()`. External apps can be loaded from SD card via `manifest.properties` files, or side-loaded as ELF binaries on ESP32 (see `app/loader.h`'s `AppLoaderApi`).
|
||||||
|
|
||||||
|
Apps can be loaded from:
|
||||||
|
|
||||||
|
- memory (`APP_LOCATION_MEMORY`)
|
||||||
|
- a path pointing to an install folder where an `.app` file was installed (`APP_LOCATION_PATH`)
|
||||||
|
- a path pointing to an `.elf` file (`APP_LOCATION_PATH`)
|
||||||
|
|
||||||
|
An app can build an optional UI via the LVGL window-manager module (see `lvgl.md`).
|
||||||
10
.claude/rules/architecture-device-model.md
Normal file
10
.claude/rules/architecture-device-model.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Architecture: Device/Driver/Module System (kernel layer, C API)
|
||||||
|
|
||||||
|
The kernel uses a Linux-inspired device model:
|
||||||
|
|
||||||
|
- **Module** (`struct Module`): loadable unit that registers drivers, hardware and symbols. Lifecycle: `module_construct` → `module_add` → `module_start`. Each device board and platform is a module.
|
||||||
|
- **Driver** (`struct Driver`): binds to devices via `compatible` strings (like devicetree). Has `start_device`/`stop_device` callbacks and an `api` pointer for type-specific operations.
|
||||||
|
- **Device** (`struct Device`): represents hardware. Lifecycle: `device_construct` → `device_add` → `device_start`. Has a parent-child tree, driver binding, and locking.
|
||||||
|
- **DeviceType** (`struct DeviceType`): enables discovering devices by category (e.g. `DISPLAY_TYPE`, `TOUCH_TYPE`, `UART_CONTROLLER_TYPE`).
|
||||||
|
|
||||||
|
Devices are defined via **devicetree** `.dts` files in each `Devices/<id>/` folder. A custom devicetree compiler (`Buildscripts/DevicetreeCompiler/compile.py`) generates C code from these files. Each device folder also has a `devicetree.yaml` specifying dependencies and the `.dts` file.
|
||||||
7
.claude/rules/architecture-layers.md
Normal file
7
.claude/rules/architecture-layers.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Architecture: Layer Stack (bottom to top)
|
||||||
|
|
||||||
|
- **TactilityKernel** — C API kernel: device/driver/module lifecycle, concurrency primitives (thread, mutex, timer, dispatcher), filesystem, logging. Header convention: `<tactility/*.h>` (lowercase snake_case).
|
||||||
|
- **TactilityFreeRtos** — Thin C++ wrappers around FreeRTOS primitives.
|
||||||
|
- **Tactility** — Main OS layer: app framework, service framework, LVGL integration, networking and services (Wi-Fi, BLE, NTP, ESP-NOW), settings, i18n.
|
||||||
|
- **TactilityC** — C bindings (`tt_*.h`) for Tactility, used by side-loaded ELF apps on ESP32. Deprecated, replaced by TactilityKernel.
|
||||||
|
- **Firmware** — Entry point (`app_main`).
|
||||||
5
.claude/rules/build-system.md
Normal file
5
.claude/rules/build-system.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Architecture: Build System
|
||||||
|
|
||||||
|
The `tactility_add_module()` CMake macro (in `Buildscripts/module.cmake`) wraps ESP-IDF's `idf_component_register` on ESP32 and standard `add_library` on POSIX, allowing the same source to build for both targets.
|
||||||
|
|
||||||
|
`device.py` reads `Devices/<id>/device.properties` and generates the `sdkconfig` file with all necessary ESP-IDF config (target chip, flash size, SPIRAM, LVGL fonts, Bluetooth, USB, etc.).
|
||||||
62
.claude/rules/building.md
Normal file
62
.claude/rules/building.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Building
|
||||||
|
|
||||||
|
## Git
|
||||||
|
|
||||||
|
The repository uses git submodules. Make sure to use `--recurse-submodules` on relevant git commands.
|
||||||
|
|
||||||
|
## Simulator (Linux/macOS, no ESP-IDF needed)
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> The simulator does **NOT** build or run on native Windows (Win32/PowerShell/cmd). This is
|
||||||
|
> a hard platform limitation, not a missing tool or PATH issue — do not attempt `cmake -B
|
||||||
|
> buildsim` on Windows, it will not work. WSL is a separate, Linux environment and is fine.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -B buildsim -G Ninja
|
||||||
|
ninja -C buildsim # build firmware + tests
|
||||||
|
./buildsim/Firmware/Tactility # run simulator
|
||||||
|
```
|
||||||
|
|
||||||
|
## ESP32 firmware
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python device.py <device-id> # generate sdkconfig for device (e.g. lilygo-tdeck)
|
||||||
|
python device.py <device-id> --dev # dev mode: force 4MB partition table
|
||||||
|
idf.py build # build firmware
|
||||||
|
idf.py flash monitor # flash and monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
Device IDs are the folder names under `Devices/` (e.g. `lilygo-tdeck`, `m5stack-cores3`, `cyd-2432s028r`).
|
||||||
|
|
||||||
|
### Windows: activating the ESP-IDF environment
|
||||||
|
|
||||||
|
On native Windows, `idf.py` is not on PATH by default — it must be activated per-shell first.
|
||||||
|
The install script places a PowerShell profile activator per IDF version at
|
||||||
|
`%IDF_TOOLS_PATH%\Microsoft.v<version>.PowerShell_profile.ps1` (path controlled by the
|
||||||
|
`IDF_TOOLS_PATH` environment variable, set to wherever ESP-IDF's tools were installed, e.g.
|
||||||
|
`C:\Espressif\tools`). Source it before running any `idf.py` command:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
. "$env:IDF_TOOLS_PATH\Microsoft.v5.5.2.PowerShell_profile.ps1" # match the installed IDF version
|
||||||
|
Set-Location "<repo-root>"
|
||||||
|
idf.py build 2>&1 | Select-Object -Last 250
|
||||||
|
```
|
||||||
|
|
||||||
|
This is Windows-specific setup (the main dev works on Linux, where `idf.py` is normally
|
||||||
|
already on PATH via `export.sh`/`. ./export.sh` or a shell profile).
|
||||||
|
|
||||||
|
## Devicetree
|
||||||
|
|
||||||
|
A device implementation has a `.dts` file.
|
||||||
|
The parser at `Buildscripts/DevicetreeCompiler/` converts DTS into C code.
|
||||||
|
It's called from the `Firmware/` build process.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Tests use Doctest and run on simulator (POSIX) target only:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -B buildsim -G Ninja
|
||||||
|
ninja -C buildsim build-tests
|
||||||
|
cd buildsim && ctest --test-dir Tests
|
||||||
|
```
|
||||||
34
.claude/rules/coding-style.md
Normal file
34
.claude/rules/coding-style.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Coding Style
|
||||||
|
|
||||||
|
Two conventions coexist; which one to use depends on the project layer:
|
||||||
|
|
||||||
|
- **C code** (TactilityKernel, drivers): `lower_snake_case` for files, functions, variables. `UpperCamelCase` for types. Files in `source/`, `include/`, `private/` directories.
|
||||||
|
- **C++ code** (Tactility, apps, services): `UpperCamelCase` for files and types. `lowerCamelCase` for functions. Files in `Source/`, `Include/`, `Private/` directories.
|
||||||
|
|
||||||
|
For projects that emit C headers and have a C++ implementation file: the internal C++ function naming should be snake_case.
|
||||||
|
|
||||||
|
Formatting is enforced by `.clang-format` (LLVM-based, 4-space indent, no column limit).
|
||||||
|
Never throw exceptions — use return types for error handling. Use `enum class` over plain `enum` when writing C++ code.
|
||||||
|
Do not add redundant null checks for parameters with an explicit non-null precondition.
|
||||||
|
|
||||||
|
Code Comments:
|
||||||
|
|
||||||
|
- Should be as short as possible, leaving only important context.
|
||||||
|
- Should avoid explaining what the code does, unless the code complexity is high enough to warrant an explanation.
|
||||||
|
- Must avoid explaining how the code was before, or how it was changed.
|
||||||
|
- Should explain why code is implemented.
|
||||||
|
- Should be as brief as possible without losing critical information.
|
||||||
|
- Should avoid explaining what was not implemented.
|
||||||
|
- Should avoid referring to designs of other subsystems.
|
||||||
|
- Must avoid interjections: avoid hyphens or braces to interject. If interjections provide crucial info, use Doxygen entity/anchor references like:
|
||||||
|
/**
|
||||||
|
* A dedicated completion \signal for one app instance's task.
|
||||||
|
* Whichever \side finishes with it last is the one that deletes `semaphore` and frees this struct.
|
||||||
|
*
|
||||||
|
* \signal Not the task's shared default FreeRTOS notification, which app_event.cpp's AppEventSubscription also uses.
|
||||||
|
* An unrelated event delivered to the same task could otherwise unblock a waiter early.
|
||||||
|
* \side The exiting task or a concurrent app_scheduler_stop() that found the entry in time and is waiting on `semaphore`.
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
31
.claude/rules/hardware-abstraction-layer.md
Normal file
31
.claude/rules/hardware-abstraction-layer.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Architecture: Hardware Abstraction Layer
|
||||||
|
|
||||||
|
## Driver
|
||||||
|
|
||||||
|
A driver generally consists of:
|
||||||
|
- Registration of driver in parent module (optional, but desirable)
|
||||||
|
- YAML bindings in the `bindings/` folder
|
||||||
|
- An `#include` that is used in the `.dts` file. The include is in `[projectname]/bindings/[drivername].h`
|
||||||
|
- The driver implementation: a `.cpp` and `.h` file. The implementation is C++, but the header exposes pure C functions. C implementations are allowed, but C++ is preferred.
|
||||||
|
|
||||||
|
Drivers are part of a kernel module.
|
||||||
|
|
||||||
|
Modules with drivers can be stored in:
|
||||||
|
- TactilityKernel
|
||||||
|
- A subproject in `Platforms` folder
|
||||||
|
- A subproject in `Devices` folder
|
||||||
|
- A subproject in `Drivers` folder
|
||||||
|
|
||||||
|
## Kernel Modules
|
||||||
|
|
||||||
|
Kernel module names are lower case and postfixed with `-module`.
|
||||||
|
|
||||||
|
Projects that are kernel modules:
|
||||||
|
|
||||||
|
1. Declare a `struct Module`
|
||||||
|
2. Contain a `devicetree.yaml` file that declares a list of dependencies (for parsing the devicetree) and specifies the bindings folder that contains the drivers' YAML definitions. For example:
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
- TactilityKernel
|
||||||
|
bindings: bindings
|
||||||
|
```
|
||||||
8
.claude/rules/key-conventions.md
Normal file
8
.claude/rules/key-conventions.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Key Conventions
|
||||||
|
|
||||||
|
- Shared cross-platform code uses `#ifdef ESP_PLATFORM` for ESP32-specific paths.
|
||||||
|
Code in `Platforms/PlatformEsp32/` is already ESP-only and does not need guards around ESP-IDF includes.
|
||||||
|
- The `Drivers/` directory contains hardware drivers (display controllers, touch controllers, PMICs, etc.) — each is its own CMake component.
|
||||||
|
- `Modules/` contains cross-cutting modules. e.g.`lvgl-module` (LVGL task management).
|
||||||
|
- `Data/system/` and `Data/data/` are flashed as FAT filesystem images on ESP32.
|
||||||
|
- Translations are in `Translations/` as CSV files, generated via `generate.py`.
|
||||||
8
.claude/rules/lvgl.md
Normal file
8
.claude/rules/lvgl.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Architecture: LVGL
|
||||||
|
|
||||||
|
User interfaces should scale well for everything between very large (e.g. 1280x720) and small (e.g. 135x240) displays. Vertical and horizontal layouts are supported.
|
||||||
|
|
||||||
|
Two kernel modules cover LVGL:
|
||||||
|
|
||||||
|
- **`lvgl-module`** (`Modules/lvgl-module/`, `<lvgl/*.h>`) owns LVGL's lifecycle: init/deinit, the LVGL task loop, and `lvgl_lock()`/`lvgl_try_lock()`/`lvgl_unlock()` mutex-based locking that any task must hold before touching LVGL objects. It bridges Tactility's device model to LVGL indevs (`lvgl/devices/*.h`: `display`, `pointer`, `keyboard`, `trackball`), and provides shared fonts (`lvgl/fonts.h`: Montserrat text sizes, Material Symbols icon sets for statusbar/launcher/shared use) and a few shared widgets (`lvgl/widgets/*.h`: `toolbar`, `spinner`, `sliderbox`).
|
||||||
|
- **`lvgl-window-manager-module`** (`Modules/lvgl-window-manager-module/`, `<lvgl_window_manager/*.h>`) manages a single stacked window per app instance on top of `lvgl-module`. `window_manager_start()`/`window_manager_stop()` create/tear down the root widget (plus optional chrome from a configured `WindowManagerScreenInitFn`); `window_manager_create()`/`window_manager_remove()` push/pop an app's window and (re)populate it via a `WindowCreateWidgetsFn`. Only the topmost window ever has live widgets - burying and resurfacing a window deletes and rebuilds its widget tree rather than hiding/showing it. That rebuild-on-remove path can run `create_widgets` on a *different* app's thread (whichever app's `window_manager_remove()` call caused this window to resurface), so `create_widgets` must only rebuild already-committed state, never decide what happens next - state transitions belong in the app's own `main()` event loop, driven by real `APP_EVENT_RESULT`s.
|
||||||
4
.claude/rules/platform-abstraction.md
Normal file
4
.claude/rules/platform-abstraction.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Architecture: Platform Abstraction
|
||||||
|
|
||||||
|
- `Platforms/platform-esp32/` — ESP-IDF specific implementations
|
||||||
|
- `Platforms/platform-posix/` — POSIX simulator implementations (SDL for display)
|
||||||
3
.claude/rules/project-overview.md
Normal file
3
.claude/rules/project-overview.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Project Overview
|
||||||
|
|
||||||
|
Tactility is an operating system for the ESP32 microcontroller family. It runs on 40+ supported devices (CYD boards, LilyGO, M5Stack, Elecrow, etc.) and includes a desktop simulator. Built with C++23, ESP-IDF, LVGL, and FreeRTOS.
|
||||||
3
.claude/rules/service-framework.md
Normal file
3
.claude/rules/service-framework.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Architecture: Service Framework
|
||||||
|
|
||||||
|
Services are a C API (`service-module`, `<service/*.h>`), not a C++ class. Each service has a `ServiceManifest` (`id`, `create_service`/`destroy_service` for its custom data, `on_start`/`on_stop` callbacks) registered via `service_manager_add()`, and is started/stopped via `service_manager_start()`/`service_manager_stop()`. Services are long-running background processes (GUI, Wi-Fi, loader, statusbar, GPS, etc.).
|
||||||
1
.claude/rules/tests.md
Normal file
1
.claude/rules/tests.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
Tests for projects should be placed in the `Tests/` or `tests/` subfolder of that project.
|
||||||
2
.github/actions/build-firmware/action.yml
vendored
2
.github/actions/build-firmware/action.yml
vendored
@ -11,7 +11,7 @@ inputs:
|
|||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: 'Board select'
|
- name: 'Board select'
|
||||||
|
|||||||
2
.github/actions/build-sdk/action.yml
vendored
2
.github/actions/build-sdk/action.yml
vendored
@ -11,7 +11,7 @@ inputs:
|
|||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: 'Board select'
|
- name: 'Board select'
|
||||||
|
|||||||
2
.github/actions/build-simulator/action.yml
vendored
2
.github/actions/build-simulator/action.yml
vendored
@ -15,7 +15,7 @@ runs:
|
|||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout repo"
|
- name: "Checkout repo"
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Install Linux Dependencies for SDL
|
- name: Install Linux Dependencies for SDL
|
||||||
|
|||||||
8
.github/workflows/build-simulator.yml
vendored
8
.github/workflows/build-simulator.yml
vendored
@ -10,7 +10,9 @@ jobs:
|
|||||||
Linux:
|
Linux:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
uses: ./.github/actions/build-simulator
|
uses: ./.github/actions/build-simulator
|
||||||
with:
|
with:
|
||||||
@ -20,7 +22,9 @@ jobs:
|
|||||||
macOS:
|
macOS:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: "Build"
|
- name: "Build"
|
||||||
uses: ./.github/actions/build-simulator
|
uses: ./.github/actions/build-simulator
|
||||||
with:
|
with:
|
||||||
|
|||||||
82
.github/workflows/build.yml
vendored
82
.github/workflows/build.yml
vendored
@ -22,66 +22,34 @@ jobs:
|
|||||||
]
|
]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: "Build SDK"
|
- name: "Build SDK"
|
||||||
uses: ./.github/actions/build-sdk
|
uses: ./.github/actions/build-sdk
|
||||||
with:
|
with:
|
||||||
board_id: ${{ matrix.board.id }}
|
board_id: ${{ matrix.board.id }}
|
||||||
arch: ${{ matrix.board.arch }}
|
arch: ${{ matrix.board.arch }}
|
||||||
BuildFirmware:
|
GenerateDeviceMatrix:
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
board: [
|
|
||||||
{ id: btt-panda-touch, arch: esp32s3 },
|
|
||||||
{ id: cyd-2432s024c, arch: esp32 },
|
|
||||||
{ id: cyd-2432s024r, arch: esp32 },
|
|
||||||
{ id: cyd-2432s028r, arch: esp32 },
|
|
||||||
{ id: cyd-2432s028rv3, arch: esp32 },
|
|
||||||
{ id: cyd-e32r28t, arch: esp32 },
|
|
||||||
{ id: cyd-e32r32p, arch: esp32 },
|
|
||||||
{ id: cyd-2432s032c, arch: esp32 },
|
|
||||||
{ id: cyd-3248s035c, arch: esp32 },
|
|
||||||
{ id: cyd-8048s043c, arch: esp32s3 },
|
|
||||||
{ id: cyd-4848s040c, arch: esp32s3 },
|
|
||||||
{ id: elecrow-crowpanel-advance-28, arch: esp32s3 },
|
|
||||||
{ id: elecrow-crowpanel-advance-35, arch: esp32s3 },
|
|
||||||
{ id: elecrow-crowpanel-advance-50, arch: esp32s3 },
|
|
||||||
{ id: elecrow-crowpanel-basic-28, arch: esp32 },
|
|
||||||
{ id: elecrow-crowpanel-basic-35, arch: esp32 },
|
|
||||||
{ id: elecrow-crowpanel-basic-50, arch: esp32s3 },
|
|
||||||
{ id: guition-jc1060p470ciwy, arch: esp32p4 },
|
|
||||||
{ id: guition-jc2432w328c, arch: esp32 },
|
|
||||||
{ id: guition-jc3248w535c, arch: esp32s3 },
|
|
||||||
{ id: guition-jc8048w550c, arch: esp32s3 },
|
|
||||||
{ id: heltec-wifi-lora-32-v3, arch: esp32s3 },
|
|
||||||
{ id: lilygo-tdeck, arch: esp32s3 },
|
|
||||||
{ id: lilygo-thmi, arch: esp32s3 },
|
|
||||||
{ id: lilygo-tdongle-s3, arch: esp32s3 },
|
|
||||||
{ id: lilygo-tdisplay-s3, arch: esp32s3 },
|
|
||||||
{ id: lilygo-tlora-pager, arch: esp32s3 },
|
|
||||||
{ id: lilygo-tdisplay, arch: esp32 },
|
|
||||||
{ id: m5stack-cardputer, arch: esp32s3 },
|
|
||||||
{ id: m5stack-cardputer-adv, arch: esp32s3 },
|
|
||||||
{ id: m5stack-core2, arch: esp32 },
|
|
||||||
{ id: m5stack-cores3, arch: esp32s3 },
|
|
||||||
{ id: m5stack-papers3, arch: esp32s3 },
|
|
||||||
{ id: m5stack-stackchan, arch: esp32s3 },
|
|
||||||
{ id: m5stack-stickc-plus, arch: esp32 },
|
|
||||||
{ id: m5stack-stickc-plus2, arch: esp32 },
|
|
||||||
{ id: m5stack-sticks3, arch: esp32s3 },
|
|
||||||
{ id: m5stack-tab5, arch: esp32p4 },
|
|
||||||
{ id: unphone, arch: esp32s3 },
|
|
||||||
{ id: waveshare-esp32-s3-geek, arch: esp32s3 },
|
|
||||||
{ id: waveshare-s3-lcd-13, arch: esp32s3 },
|
|
||||||
{ id: waveshare-s3-touch-lcd-128, arch: esp32s3 },
|
|
||||||
{ id: waveshare-s3-touch-lcd-147, arch: esp32s3 },
|
|
||||||
{ id: waveshare-s3-touch-lcd-43, arch: esp32s3 },
|
|
||||||
{ id: wireless-tag-wt32-sc01-plus, arch: esp32s3 }
|
|
||||||
]
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [ BuildSdk ]
|
needs: [ BuildSdk ]
|
||||||
|
outputs:
|
||||||
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- id: set-matrix
|
||||||
|
run: python3 Buildscripts/gh-generate-device-matrix.py
|
||||||
|
BuildFirmware:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: GenerateDeviceMatrix
|
||||||
|
strategy:
|
||||||
|
matrix: ${{ fromJSON(needs.GenerateDeviceMatrix.outputs.matrix) }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: "Build Firmware"
|
- name: "Build Firmware"
|
||||||
uses: ./.github/actions/build-firmware
|
uses: ./.github/actions/build-firmware
|
||||||
with:
|
with:
|
||||||
@ -94,7 +62,9 @@ jobs:
|
|||||||
(github.event_name == 'push' && github.ref == 'refs/heads/main') ||
|
(github.event_name == 'push' && github.ref == 'refs/heads/main') ||
|
||||||
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v'))
|
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v'))
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: "Bundle Artifacts"
|
- name: "Bundle Artifacts"
|
||||||
uses: ./.github/actions/bundle-artifacts
|
uses: ./.github/actions/bundle-artifacts
|
||||||
PublishFirmwareSnapshot:
|
PublishFirmwareSnapshot:
|
||||||
@ -102,7 +72,7 @@ jobs:
|
|||||||
needs: [ BundleArtifacts ]
|
needs: [ BundleArtifacts ]
|
||||||
if: (github.event_name == 'push' && github.ref == 'refs/heads/main')
|
if: (github.event_name == 'push' && github.ref == 'refs/heads/main')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- name: "Publish Firmware Snapshot"
|
- name: "Publish Firmware Snapshot"
|
||||||
env:
|
env:
|
||||||
CDN_ID: ${{ secrets.CDN_ID }}
|
CDN_ID: ${{ secrets.CDN_ID }}
|
||||||
@ -116,7 +86,7 @@ jobs:
|
|||||||
needs: [ BundleArtifacts ]
|
needs: [ BundleArtifacts ]
|
||||||
if: (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v'))
|
if: (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v'))
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- name: "Publish Firmware Stable"
|
- name: "Publish Firmware Stable"
|
||||||
env:
|
env:
|
||||||
CDN_ID: ${{ secrets.CDN_ID }}
|
CDN_ID: ${{ secrets.CDN_ID }}
|
||||||
@ -130,7 +100,7 @@ jobs:
|
|||||||
needs: [ BundleArtifacts ]
|
needs: [ BundleArtifacts ]
|
||||||
if: (github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
|
if: (github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- name: "Publish SDKs"
|
- name: "Publish SDKs"
|
||||||
env:
|
env:
|
||||||
CDN_ID: ${{ secrets.CDN_ID }}
|
CDN_ID: ${{ secrets.CDN_ID }}
|
||||||
|
|||||||
16
.github/workflows/tests.yml
vendored
16
.github/workflows/tests.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout repo"
|
- name: "Checkout repo"
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: "Configure Project"
|
- name: "Configure Project"
|
||||||
@ -19,20 +19,14 @@ jobs:
|
|||||||
run: cmake -S ./ -B build
|
run: cmake -S ./ -B build
|
||||||
- name: "Build Tests"
|
- name: "Build Tests"
|
||||||
run: cmake --build build --target build-tests
|
run: cmake --build build --target build-tests
|
||||||
- name: "Run TactilityCore Tests"
|
- name: "Run Tests"
|
||||||
run: build/Tests/TactilityCore/TactilityCoreTests
|
run: ctest --test-dir build/Tests
|
||||||
- name: "Run TactilityFreeRtos Tests"
|
|
||||||
run: build/Tests/TactilityFreeRtos/TactilityFreeRtosTests
|
|
||||||
- name: "Run TactilityHeadless Tests"
|
|
||||||
run: build/Tests/Tactility/TactilityTests
|
|
||||||
- name: "Run TactilityKernel Tests"
|
|
||||||
run: build/Tests/TactilityKernel/TactilityKernelTests
|
|
||||||
DevicetreeTests:
|
DevicetreeTests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout repo"
|
- uses: actions/checkout@v6
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
with:
|
||||||
|
persist-credentials: false
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: "Install Python Dependencies"
|
- name: "Install Python Dependencies"
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,9 +1,8 @@
|
|||||||
.idea/
|
.idea/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
build/
|
build*/
|
||||||
buildsim/
|
cmake*/
|
||||||
cmake-*/
|
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
*.cbp
|
*.cbp
|
||||||
CMakeFiles
|
CMakeFiles
|
||||||
|
|||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -16,3 +16,6 @@
|
|||||||
[submodule "Libraries/cJSON/cJSON"]
|
[submodule "Libraries/cJSON/cJSON"]
|
||||||
path = Libraries/cJSON/cJSON
|
path = Libraries/cJSON/cJSON
|
||||||
url = https://github.com/DaveGamble/cJSON.git
|
url = https://github.com/DaveGamble/cJSON.git
|
||||||
|
[submodule "Libraries/esp_epaper"]
|
||||||
|
path = Libraries/esp_epaper
|
||||||
|
url = https://github.com/NellowTCS/esp_epaper.git
|
||||||
|
|||||||
@ -2,11 +2,9 @@ import subprocess
|
|||||||
from datetime import datetime, UTC
|
from datetime import datetime, UTC
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import configparser
|
|
||||||
from dataclasses import dataclass, asdict
|
from dataclasses import dataclass, asdict
|
||||||
import json
|
import json
|
||||||
import shutil
|
import shutil
|
||||||
from configparser import RawConfigParser
|
|
||||||
|
|
||||||
VERBOSE = False
|
VERBOSE = False
|
||||||
DEVICES_FOLDER = "Devices"
|
DEVICES_FOLDER = "Devices"
|
||||||
@ -68,32 +66,29 @@ def exit_with_error(message):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def read_properties_file(path):
|
def read_properties_file(path):
|
||||||
config = configparser.RawConfigParser()
|
properties = {}
|
||||||
# Don't convert keys to lowercase
|
with open(path, "r") as file:
|
||||||
config.optionxform = str
|
for line in file:
|
||||||
config.read(path)
|
stripped = line.strip()
|
||||||
return config
|
if not stripped or stripped.startswith("#"):
|
||||||
|
continue
|
||||||
|
key, sep, value = line.partition("=")
|
||||||
|
if not sep:
|
||||||
|
continue
|
||||||
|
properties[key.strip()] = value.strip()
|
||||||
|
return properties
|
||||||
|
|
||||||
def get_property_or_none(properties: RawConfigParser, group: str, key: str):
|
def get_property_or_none(properties: dict, group: str, key: str):
|
||||||
if group not in properties.sections():
|
return properties.get(f"{group}.{key}")
|
||||||
return None
|
|
||||||
if key not in properties[group].keys():
|
|
||||||
return None
|
|
||||||
return properties[group][key]
|
|
||||||
|
|
||||||
def get_boolean_property_or_false(properties: RawConfigParser, group: str, key: str):
|
def get_boolean_property_or_false(properties: dict, group: str, key: str):
|
||||||
if group not in properties.sections():
|
return properties.get(f"{group}.{key}") == "true"
|
||||||
return False
|
|
||||||
if key not in properties[group].keys():
|
|
||||||
return False
|
|
||||||
return properties[group][key] == "true"
|
|
||||||
|
|
||||||
def get_property_or_exit(properties: RawConfigParser, group: str, key: str):
|
def get_property_or_exit(properties: dict, group: str, key: str):
|
||||||
if group not in properties.sections():
|
full_key = f"{group}.{key}"
|
||||||
exit_with_error(f"Device properties does not contain group: {group}")
|
if full_key not in properties:
|
||||||
if key not in properties[group].keys():
|
exit_with_error(f"Device properties does not contain key: {full_key}")
|
||||||
exit_with_error(f"Device properties does not contain key: {key}")
|
return properties[full_key]
|
||||||
return properties[group][key]
|
|
||||||
|
|
||||||
def read_device_properties(device_id):
|
def read_device_properties(device_id):
|
||||||
mapping_file_path = os.path.join(DEVICES_FOLDER, device_id, "device.properties")
|
mapping_file_path = os.path.join(DEVICES_FOLDER, device_id, "device.properties")
|
||||||
@ -129,7 +124,7 @@ def to_manifest_chip_name(name):
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def process_device(in_path: str, out_path: str, device_directory: str, device_id: str, device_properties: RawConfigParser, version: str):
|
def process_device(in_path: str, out_path: str, device_directory: str, device_id: str, device_properties: dict, version: str):
|
||||||
in_device_path = os.path.join(in_path, device_directory)
|
in_device_path = os.path.join(in_path, device_directory)
|
||||||
in_device_binaries_path = os.path.join(in_device_path, "Binaries")
|
in_device_binaries_path = os.path.join(in_device_path, "Binaries")
|
||||||
if not os.path.isdir(in_device_binaries_path):
|
if not os.path.isdir(in_device_binaries_path):
|
||||||
|
|||||||
@ -46,6 +46,8 @@ def parse_binding(file_path: str, binding_dirs: list[str]) -> Binding:
|
|||||||
description=details.get('description', '').strip(),
|
description=details.get('description', '').strip(),
|
||||||
default=details.get('default', None),
|
default=details.get('default', None),
|
||||||
element_type=details.get('element-type', None),
|
element_type=details.get('element-type', None),
|
||||||
|
min=details.get('min', None),
|
||||||
|
max=details.get('max', None),
|
||||||
)
|
)
|
||||||
properties_dict[name] = prop
|
properties_dict[name] = prop
|
||||||
filename = os.path.basename(file_path)
|
filename = os.path.basename(file_path)
|
||||||
|
|||||||
@ -26,7 +26,7 @@ def get_device_node_name_safe(device: Device):
|
|||||||
def get_device_type_name(device: Device, bindings: list[Binding]):
|
def get_device_type_name(device: Device, bindings: list[Binding]):
|
||||||
device_binding = find_device_binding(device, bindings)
|
device_binding = find_device_binding(device, bindings)
|
||||||
if device_binding is None:
|
if device_binding is None:
|
||||||
raise DevicetreeException(f"Binding not found for {device.node_name}")
|
raise DevicetreeException(f"Binding not found for {device.node_name}. Make sure that the driver name in the driver's yaml and driver code declarations matches with the device dts file.")
|
||||||
if device_binding.compatible is None:
|
if device_binding.compatible is None:
|
||||||
raise DevicetreeException(f"Couldn't find compatible binding for {device.node_name}")
|
raise DevicetreeException(f"Couldn't find compatible binding for {device.node_name}")
|
||||||
compatible_safe = device_binding.compatible.split(",")[-1]
|
compatible_safe = device_binding.compatible.split(",")[-1]
|
||||||
@ -79,6 +79,11 @@ def property_to_string(property: DeviceProperty, devices: list[Device]) -> str:
|
|||||||
value_list.append(str(item))
|
value_list.append(str(item))
|
||||||
return "{ " + ",".join(value_list) + " }"
|
return "{ " + ",".join(value_list) + " }"
|
||||||
elif type == "phandle":
|
elif type == "phandle":
|
||||||
|
# Mirrors the string-passthrough convention "phandles" already uses for sentinel defaults
|
||||||
|
# like GPIO_PIN_SPEC_NONE: a binding default of "NULL" represents "no device referenced",
|
||||||
|
# not an actual node name to resolve.
|
||||||
|
if isinstance(property.value, str) and property.value == "NULL":
|
||||||
|
return "NULL"
|
||||||
return find_phandle(devices, property.value)
|
return find_phandle(devices, property.value)
|
||||||
elif type == "phandles":
|
elif type == "phandles":
|
||||||
value_list = list()
|
value_list = list()
|
||||||
@ -113,6 +118,25 @@ def resolve_phandle_array_entries(device_property, devices):
|
|||||||
entries.append(str(item))
|
entries.append(str(item))
|
||||||
return entries
|
return entries
|
||||||
|
|
||||||
|
def validate_property_range(device: Device, binding_property, value) -> None:
|
||||||
|
"""Enforces a binding's optional min/max on int-typed properties. Silently skips
|
||||||
|
values that aren't parseable as a plain integer literal (e.g. a passed-through
|
||||||
|
symbolic #define) - those can't be range-checked at compile time."""
|
||||||
|
if binding_property.min is None and binding_property.max is None:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
numeric_value = int(value, 0) if isinstance(value, str) else int(value)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return
|
||||||
|
if binding_property.min is not None and numeric_value < binding_property.min:
|
||||||
|
raise DevicetreeException(
|
||||||
|
f"Device '{device.node_name}' property '{binding_property.name}' value {numeric_value} is below minimum {binding_property.min}"
|
||||||
|
)
|
||||||
|
if binding_property.max is not None and numeric_value > binding_property.max:
|
||||||
|
raise DevicetreeException(
|
||||||
|
f"Device '{device.node_name}' property '{binding_property.name}' value {numeric_value} is above maximum {binding_property.max}"
|
||||||
|
)
|
||||||
|
|
||||||
def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], devices: list[Device]) -> list:
|
def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], devices: list[Device]) -> list:
|
||||||
compatible_property = find_device_property(device, "compatible")
|
compatible_property = find_device_property(device, "compatible")
|
||||||
if compatible_property is None:
|
if compatible_property is None:
|
||||||
@ -137,7 +161,7 @@ def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], de
|
|||||||
|
|
||||||
node_name = get_device_node_name_safe(device)
|
node_name = get_device_node_name_safe(device)
|
||||||
result = []
|
result = []
|
||||||
phandle_arrays = []
|
array_decls = []
|
||||||
for binding_property in binding_properties:
|
for binding_property in binding_properties:
|
||||||
device_property = find_device_property(device, binding_property.name)
|
device_property = find_device_property(device, binding_property.name)
|
||||||
|
|
||||||
@ -148,7 +172,35 @@ def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], de
|
|||||||
array_var = f"{node_name}_{prop_safe}"
|
array_var = f"{node_name}_{prop_safe}"
|
||||||
if device_property is not None:
|
if device_property is not None:
|
||||||
entries = resolve_phandle_array_entries(device_property, devices)
|
entries = resolve_phandle_array_entries(device_property, devices)
|
||||||
phandle_arrays.append((array_var, binding_property.element_type, entries))
|
array_decls.append((array_var, binding_property.element_type, entries))
|
||||||
|
result.append(f"({binding_property.element_type}*){array_var}")
|
||||||
|
result.append(str(len(entries)))
|
||||||
|
elif binding_property.default is not None:
|
||||||
|
result.append("NULL")
|
||||||
|
result.append("0")
|
||||||
|
elif binding_property.required:
|
||||||
|
raise DevicetreeException(f"device {device.node_name} doesn't have property '{binding_property.name}'")
|
||||||
|
else:
|
||||||
|
result.append("NULL")
|
||||||
|
result.append("0")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if binding_property.type == "array":
|
||||||
|
# A flat literal array (DTS `[ ... ]` syntax, e.g. a byte blob), as opposed to
|
||||||
|
# phandle-array's list of resolved device references. Emits the same
|
||||||
|
# (pointer, length) parameter pair, backed by a plain data array instead of one
|
||||||
|
# holding phandle-derived initializers.
|
||||||
|
if binding_property.element_type is None:
|
||||||
|
raise DevicetreeException(f"array property '{binding_property.name}' requires 'element-type' in binding")
|
||||||
|
prop_safe = binding_property.name.replace("-", "_")
|
||||||
|
array_var = f"{node_name}_{prop_safe}"
|
||||||
|
if device_property is not None:
|
||||||
|
if device_property.type != "array":
|
||||||
|
raise DevicetreeException(
|
||||||
|
f"Device '{device.node_name}' property '{binding_property.name}' must use '[ ... ]' array syntax"
|
||||||
|
)
|
||||||
|
entries = [str(value) for value in device_property.value]
|
||||||
|
array_decls.append((array_var, binding_property.element_type, entries))
|
||||||
result.append(f"({binding_property.element_type}*){array_var}")
|
result.append(f"({binding_property.element_type}*){array_var}")
|
||||||
result.append(str(len(entries)))
|
result.append(str(len(entries)))
|
||||||
elif binding_property.default is not None:
|
elif binding_property.default is not None:
|
||||||
@ -163,6 +215,7 @@ def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], de
|
|||||||
|
|
||||||
if device_property is None:
|
if device_property is None:
|
||||||
if binding_property.default is not None:
|
if binding_property.default is not None:
|
||||||
|
validate_property_range(device, binding_property, binding_property.default)
|
||||||
temp_prop = DeviceProperty(
|
temp_prop = DeviceProperty(
|
||||||
name=binding_property.name,
|
name=binding_property.name,
|
||||||
type=binding_property.type,
|
type=binding_property.type,
|
||||||
@ -179,19 +232,20 @@ def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], de
|
|||||||
else:
|
else:
|
||||||
raise DevicetreeException(f"Device {device.node_name} doesn't have property '{binding_property.name}' and no default value is set")
|
raise DevicetreeException(f"Device {device.node_name} doesn't have property '{binding_property.name}' and no default value is set")
|
||||||
else:
|
else:
|
||||||
|
validate_property_range(device, binding_property, device_property.value)
|
||||||
result.append(property_to_string(device_property, devices))
|
result.append(property_to_string(device_property, devices))
|
||||||
|
|
||||||
return result, phandle_arrays
|
return result, array_decls
|
||||||
|
|
||||||
def write_config(file, device: Device, bindings: list[Binding], devices: list[Device], type_name: str):
|
def write_config(file, device: Device, bindings: list[Binding], devices: list[Device], type_name: str):
|
||||||
node_name = get_device_node_name_safe(device)
|
node_name = get_device_node_name_safe(device)
|
||||||
config_type = f"{type_name}_config_dt"
|
config_type = f"{type_name}_config_dt"
|
||||||
config_variable_name = f"{node_name}_config"
|
config_variable_name = f"{node_name}_config"
|
||||||
|
|
||||||
config_params, phandle_arrays = resolve_parameters_from_bindings(device, bindings, devices)
|
config_params, array_decls = resolve_parameters_from_bindings(device, bindings, devices)
|
||||||
|
|
||||||
# Write phandle-array variables before the config struct
|
# Write phandle-array/array variables before the config struct
|
||||||
for array_var, element_type, entries in phandle_arrays:
|
for array_var, element_type, entries in array_decls:
|
||||||
entries_str = ", ".join(entries)
|
entries_str = ", ".join(entries)
|
||||||
file.write(f"static {element_type} {array_var}[] = {{ {entries_str} }};\n")
|
file.write(f"static {element_type} {array_var}[] = {{ {entries_str} }};\n")
|
||||||
|
|
||||||
@ -228,6 +282,7 @@ def write_device_structs(file, device: Device, parent_device: Device, bindings:
|
|||||||
file.write(f"\t.address = {address_value},\n")
|
file.write(f"\t.address = {address_value},\n")
|
||||||
file.write(f"\t.name = \"{device.node_name}\",\n") # Use original name
|
file.write(f"\t.name = \"{device.node_name}\",\n") # Use original name
|
||||||
file.write(f"\t.config = &{config_variable_name},\n")
|
file.write(f"\t.config = &{config_variable_name},\n")
|
||||||
|
file.write("\t.flags = DEVICE_FLAG_DTS,\n")
|
||||||
file.write(f"\t.parent = {parent_value},\n")
|
file.write(f"\t.parent = {parent_value},\n")
|
||||||
file.write("\t.internal = NULL\n")
|
file.write("\t.internal = NULL\n")
|
||||||
file.write("};\n\n")
|
file.write("};\n\n")
|
||||||
@ -295,7 +350,7 @@ def generate_devicetree_c(filename: str, items: list[object], bindings: list[Bin
|
|||||||
for item in items:
|
for item in items:
|
||||||
if type(item) is Device:
|
if type(item) is Device:
|
||||||
write_device_structs(file, item, None, bindings, devices, verbose)
|
write_device_structs(file, item, None, bindings, devices, verbose)
|
||||||
file.write("struct DtsDevice dts_devices[] = {\n")
|
file.write("const struct DtsDevice dts_devices[] = {\n")
|
||||||
for item in items:
|
for item in items:
|
||||||
if type(item) is Device:
|
if type(item) is Device:
|
||||||
write_device_list_entry(file, item, bindings, verbose)
|
write_device_list_entry(file, item, bindings, verbose)
|
||||||
@ -324,7 +379,7 @@ def generate_devicetree_c(filename: str, items: list[object], bindings: list[Bin
|
|||||||
file.write(f"extern struct Module {symbol};\n")
|
file.write(f"extern struct Module {symbol};\n")
|
||||||
file.write("\n")
|
file.write("\n")
|
||||||
# Create array of symbol variables
|
# Create array of symbol variables
|
||||||
file.write("struct Module* dts_modules[] = {\n")
|
file.write("struct Module* const dts_modules[] = {\n")
|
||||||
for symbol in module_symbol_names:
|
for symbol in module_symbol_names:
|
||||||
file.write(f"\t&{symbol},\n")
|
file.write(f"\t&{symbol},\n")
|
||||||
file.write("\tNULL\n")
|
file.write("\tNULL\n")
|
||||||
@ -342,10 +397,10 @@ def generate_devicetree_h(filename: str):
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Array of device tree modules terminated with DTS_MODULE_TERMINATOR
|
// Array of device tree modules terminated with DTS_MODULE_TERMINATOR
|
||||||
extern struct DtsDevice dts_devices[];
|
extern const struct DtsDevice dts_devices[];
|
||||||
|
|
||||||
// Array of module symbols terminated with NULL
|
// Array of module symbols terminated with NULL
|
||||||
extern struct Module* dts_modules[];
|
extern struct Module* const dts_modules[];
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,8 @@ class BindingProperty:
|
|||||||
description: str
|
description: str
|
||||||
default: object = None
|
default: object = None
|
||||||
element_type: str = None
|
element_type: str = None
|
||||||
|
min: object = None
|
||||||
|
max: object = None
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Binding:
|
class Binding:
|
||||||
|
|||||||
@ -13,6 +13,7 @@ static struct Device root = {
|
|||||||
.address = 0,
|
.address = 0,
|
||||||
.name = "/",
|
.name = "/",
|
||||||
.config = &root_config,
|
.config = &root_config,
|
||||||
|
.flags = DEVICE_FLAG_DTS,
|
||||||
.parent = NULL,
|
.parent = NULL,
|
||||||
.internal = NULL
|
.internal = NULL
|
||||||
};
|
};
|
||||||
@ -27,6 +28,7 @@ static struct Device test_device = {
|
|||||||
.address = 0,
|
.address = 0,
|
||||||
.name = "test-device",
|
.name = "test-device",
|
||||||
.config = &test_device_config,
|
.config = &test_device_config,
|
||||||
|
.flags = DEVICE_FLAG_DTS,
|
||||||
.parent = &root,
|
.parent = &root,
|
||||||
.internal = NULL
|
.internal = NULL
|
||||||
};
|
};
|
||||||
@ -43,11 +45,12 @@ static struct Device bool_test_device = {
|
|||||||
.address = 0,
|
.address = 0,
|
||||||
.name = "bool-test-device",
|
.name = "bool-test-device",
|
||||||
.config = &bool_test_device_config,
|
.config = &bool_test_device_config,
|
||||||
|
.flags = DEVICE_FLAG_DTS,
|
||||||
.parent = &root,
|
.parent = &root,
|
||||||
.internal = NULL
|
.internal = NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DtsDevice dts_devices[] = {
|
const struct DtsDevice dts_devices[] = {
|
||||||
{ &root, "test,root", DTS_DEVICE_STATUS_OKAY },
|
{ &root, "test,root", DTS_DEVICE_STATUS_OKAY },
|
||||||
{ &test_device, "test,generic-device", DTS_DEVICE_STATUS_OKAY },
|
{ &test_device, "test,generic-device", DTS_DEVICE_STATUS_OKAY },
|
||||||
{ &bool_test_device, "test,bool-device", DTS_DEVICE_STATUS_OKAY },
|
{ &bool_test_device, "test,bool-device", DTS_DEVICE_STATUS_OKAY },
|
||||||
@ -56,7 +59,7 @@ struct DtsDevice dts_devices[] = {
|
|||||||
|
|
||||||
extern struct Module data_module;
|
extern struct Module data_module;
|
||||||
|
|
||||||
struct Module* dts_modules[] = {
|
struct Module* const dts_modules[] = {
|
||||||
&data_module,
|
&data_module,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,10 +7,10 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Array of device tree modules terminated with DTS_MODULE_TERMINATOR
|
// Array of device tree modules terminated with DTS_MODULE_TERMINATOR
|
||||||
extern struct DtsDevice dts_devices[];
|
extern const struct DtsDevice dts_devices[];
|
||||||
|
|
||||||
// Array of module symbols terminated with NULL
|
// Array of module symbols terminated with NULL
|
||||||
extern struct Module* dts_modules[];
|
extern struct Module* const dts_modules[];
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,6 +76,225 @@ def test_compile_invalid_dts():
|
|||||||
print("PASSED")
|
print("PASSED")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def write_minmax_config(tmp_dir, device_property_line, binding_min=0, binding_max=3, binding_default=1):
|
||||||
|
config_dir = os.path.join(tmp_dir, "minmax_data")
|
||||||
|
bindings_dir = os.path.join(config_dir, "bindings")
|
||||||
|
os.makedirs(bindings_dir)
|
||||||
|
|
||||||
|
with open(os.path.join(config_dir, "devicetree.yaml"), "w") as f:
|
||||||
|
f.write("dts: test.dts\nbindings: bindings")
|
||||||
|
|
||||||
|
with open(os.path.join(config_dir, "test.dts"), "w") as f:
|
||||||
|
f.write(f"""/dts-v1/;
|
||||||
|
|
||||||
|
/ {{
|
||||||
|
compatible = "test,root";
|
||||||
|
model = "Test Model";
|
||||||
|
|
||||||
|
test-device@0 {{
|
||||||
|
compatible = "test,minmax-device";
|
||||||
|
{device_property_line}
|
||||||
|
}};
|
||||||
|
}};
|
||||||
|
""")
|
||||||
|
|
||||||
|
with open(os.path.join(bindings_dir, "test,root.yaml"), "w") as f:
|
||||||
|
f.write("description: Test root binding\ncompatible: \"test,root\"\nproperties:\n model:\n type: string\n")
|
||||||
|
|
||||||
|
with open(os.path.join(bindings_dir, "test,minmax-device.yaml"), "w") as f:
|
||||||
|
f.write(f"""description: Test min/max binding
|
||||||
|
compatible: "test,minmax-device"
|
||||||
|
properties:
|
||||||
|
ranged-prop:
|
||||||
|
type: int
|
||||||
|
default: {binding_default}
|
||||||
|
min: {binding_min}
|
||||||
|
max: {binding_max}
|
||||||
|
""")
|
||||||
|
|
||||||
|
return config_dir
|
||||||
|
|
||||||
|
def test_minmax_within_range_succeeds():
|
||||||
|
print("Running test_minmax_within_range_succeeds...")
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <2>;")
|
||||||
|
output_dir = os.path.join(tmp_dir, "output")
|
||||||
|
os.makedirs(output_dir)
|
||||||
|
|
||||||
|
result = run_compiler(config_dir, output_dir)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"FAILED: Compilation should have succeeded: {result.stderr} {result.stdout}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("PASSED")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def test_minmax_below_minimum_fails():
|
||||||
|
print("Running test_minmax_below_minimum_fails...")
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <-1>;")
|
||||||
|
output_dir = os.path.join(tmp_dir, "output")
|
||||||
|
os.makedirs(output_dir)
|
||||||
|
|
||||||
|
result = run_compiler(config_dir, output_dir)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
print("FAILED: Compilation should have failed for a below-minimum value")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if "below minimum" not in result.stdout:
|
||||||
|
print(f"FAILED: Expected 'below minimum' error message, got: {result.stdout}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("PASSED")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def test_minmax_above_maximum_fails():
|
||||||
|
print("Running test_minmax_above_maximum_fails...")
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <7>;")
|
||||||
|
output_dir = os.path.join(tmp_dir, "output")
|
||||||
|
os.makedirs(output_dir)
|
||||||
|
|
||||||
|
result = run_compiler(config_dir, output_dir)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
print("FAILED: Compilation should have failed for an above-maximum value")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if "above maximum" not in result.stdout:
|
||||||
|
print(f"FAILED: Expected 'above maximum' error message, got: {result.stdout}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("PASSED")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def test_minmax_out_of_range_default_fails():
|
||||||
|
print("Running test_minmax_out_of_range_default_fails...")
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
# Property omitted from the .dts entirely, so the (invalid) binding default is used.
|
||||||
|
config_dir = write_minmax_config(tmp_dir, "", binding_default=9)
|
||||||
|
output_dir = os.path.join(tmp_dir, "output")
|
||||||
|
os.makedirs(output_dir)
|
||||||
|
|
||||||
|
result = run_compiler(config_dir, output_dir)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
print("FAILED: Compilation should have failed for an out-of-range default value")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if "above maximum" not in result.stdout:
|
||||||
|
print(f"FAILED: Expected 'above maximum' error message, got: {result.stdout}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("PASSED")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def test_minmax_symbolic_value_skips_validation():
|
||||||
|
print("Running test_minmax_symbolic_value_skips_validation...")
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
# A passed-through symbolic constant can't be range-checked at compile time and must
|
||||||
|
# not be rejected just because a min/max is declared.
|
||||||
|
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <SOME_DEFINE>;")
|
||||||
|
output_dir = os.path.join(tmp_dir, "output")
|
||||||
|
os.makedirs(output_dir)
|
||||||
|
|
||||||
|
result = run_compiler(config_dir, output_dir)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"FAILED: Compilation should have succeeded for a symbolic value: {result.stderr} {result.stdout}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("PASSED")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def write_array_config(tmp_dir, device_property_line):
|
||||||
|
config_dir = os.path.join(tmp_dir, "array_data")
|
||||||
|
bindings_dir = os.path.join(config_dir, "bindings")
|
||||||
|
os.makedirs(bindings_dir)
|
||||||
|
|
||||||
|
with open(os.path.join(config_dir, "devicetree.yaml"), "w") as f:
|
||||||
|
f.write("dts: test.dts\nbindings: bindings")
|
||||||
|
|
||||||
|
with open(os.path.join(config_dir, "test.dts"), "w") as f:
|
||||||
|
f.write(f"""/dts-v1/;
|
||||||
|
|
||||||
|
/ {{
|
||||||
|
compatible = "test,root";
|
||||||
|
model = "Test Model";
|
||||||
|
|
||||||
|
test-device@0 {{
|
||||||
|
compatible = "test,array-device";
|
||||||
|
{device_property_line}
|
||||||
|
}};
|
||||||
|
}};
|
||||||
|
""")
|
||||||
|
|
||||||
|
with open(os.path.join(bindings_dir, "test,root.yaml"), "w") as f:
|
||||||
|
f.write("description: Test root binding\ncompatible: \"test,root\"\nproperties:\n model:\n type: string\n")
|
||||||
|
|
||||||
|
with open(os.path.join(bindings_dir, "test,array-device.yaml"), "w") as f:
|
||||||
|
f.write("""description: Test array binding
|
||||||
|
compatible: "test,array-device"
|
||||||
|
properties:
|
||||||
|
init-sequence:
|
||||||
|
type: array
|
||||||
|
element-type: uint8_t
|
||||||
|
""")
|
||||||
|
|
||||||
|
return config_dir
|
||||||
|
|
||||||
|
def test_array_property_generates_static_array_and_length():
|
||||||
|
print("Running test_array_property_generates_static_array_and_length...")
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
config_dir = write_array_config(tmp_dir, "init-sequence = [0xFF 0x01 0x00 0x00 0x10 5 0];")
|
||||||
|
output_dir = os.path.join(tmp_dir, "output")
|
||||||
|
os.makedirs(output_dir)
|
||||||
|
|
||||||
|
result = run_compiler(config_dir, output_dir)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"FAILED: Compilation should have succeeded: {result.stderr} {result.stdout}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
with open(os.path.join(output_dir, "devicetree.c")) as f:
|
||||||
|
generated = f.read()
|
||||||
|
|
||||||
|
if "static uint8_t test_device_init_sequence[] = { 0xFF, 0x01, 0x00, 0x00, 0x10, 5, 0 };" not in generated:
|
||||||
|
print(f"FAILED: Expected static array declaration not found:\n{generated}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if "(uint8_t*)test_device_init_sequence" not in generated or "\t7\n" not in generated:
|
||||||
|
print(f"FAILED: Expected (pointer, length) config params not found:\n{generated}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("PASSED")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def test_array_property_defaults_to_null_when_absent():
|
||||||
|
print("Running test_array_property_defaults_to_null_when_absent...")
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
config_dir = write_array_config(tmp_dir, "")
|
||||||
|
output_dir = os.path.join(tmp_dir, "output")
|
||||||
|
os.makedirs(output_dir)
|
||||||
|
|
||||||
|
result = run_compiler(config_dir, output_dir)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"FAILED: Compilation should have succeeded: {result.stderr} {result.stdout}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
with open(os.path.join(output_dir, "devicetree.c")) as f:
|
||||||
|
generated = f.read()
|
||||||
|
|
||||||
|
if "NULL,\n\t0" not in generated:
|
||||||
|
print(f"FAILED: Expected NULL/0 defaults not found:\n{generated}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("PASSED")
|
||||||
|
return True
|
||||||
|
|
||||||
def test_compile_missing_config():
|
def test_compile_missing_config():
|
||||||
print("Running test_compile_missing_config...")
|
print("Running test_compile_missing_config...")
|
||||||
with tempfile.TemporaryDirectory() as output_dir:
|
with tempfile.TemporaryDirectory() as output_dir:
|
||||||
@ -96,7 +315,14 @@ if __name__ == "__main__":
|
|||||||
tests = [
|
tests = [
|
||||||
test_compile_success,
|
test_compile_success,
|
||||||
test_compile_invalid_dts,
|
test_compile_invalid_dts,
|
||||||
test_compile_missing_config
|
test_compile_missing_config,
|
||||||
|
test_minmax_within_range_succeeds,
|
||||||
|
test_minmax_below_minimum_fails,
|
||||||
|
test_minmax_above_maximum_fails,
|
||||||
|
test_minmax_out_of_range_default_fails,
|
||||||
|
test_minmax_symbolic_value_skips_validation,
|
||||||
|
test_array_property_generates_static_array_and_length,
|
||||||
|
test_array_property_defaults_to_null_when_absent
|
||||||
]
|
]
|
||||||
|
|
||||||
failed = 0
|
failed = 0
|
||||||
|
|||||||
@ -13,9 +13,9 @@ $jsonClean = $json.flash_files -replace '[\{\}\@\;]', ''
|
|||||||
$jsonClean = $jsonClean -replace '[\=]', ' '
|
$jsonClean = $jsonClean -replace '[\=]', ' '
|
||||||
|
|
||||||
cd Binaries
|
cd Binaries
|
||||||
$command = "esptool --port $port erase_flash"
|
$command = "esptool --port $port erase-flash"
|
||||||
Invoke-Expression $command
|
Invoke-Expression $command
|
||||||
$command = "esptool --port $port write_flash $jsonClean"
|
$command = "esptool --port $port write-flash $jsonClean"
|
||||||
Invoke-Expression $command
|
Invoke-Expression $command
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
|||||||
@ -49,7 +49,7 @@ fi
|
|||||||
# Take the flash_arg file contents and join each line in the file into a single line
|
# Take the flash_arg file contents and join each line in the file into a single line
|
||||||
flash_args=`grep \n Binaries/flash_args | awk '{print}' ORS=' '`
|
flash_args=`grep \n Binaries/flash_args | awk '{print}' ORS=' '`
|
||||||
cd Binaries
|
cd Binaries
|
||||||
$esptoolPath --port $1 erase_flash
|
$esptoolPath --port $1 erase-flash
|
||||||
$esptoolPath --port $1 write_flash $flash_args
|
$esptoolPath --port $1 write-flash $flash_args
|
||||||
cd -
|
cd -
|
||||||
|
|
||||||
|
|||||||
@ -2,23 +2,23 @@ idf_component_register(
|
|||||||
INCLUDE_DIRS
|
INCLUDE_DIRS
|
||||||
"Libraries/TactilityC/include"
|
"Libraries/TactilityC/include"
|
||||||
"Libraries/TactilityKernel/include"
|
"Libraries/TactilityKernel/include"
|
||||||
"Libraries/TactilityFreeRtos/include"
|
"Libraries/TactilityFreeRtos/Include"
|
||||||
"Libraries/lvgl/include"
|
"Libraries/lvgl/include"
|
||||||
|
"Libraries/minmea/include"
|
||||||
|
"Libraries/minitar/include"
|
||||||
"Modules/lvgl-module/include"
|
"Modules/lvgl-module/include"
|
||||||
"Drivers/bm8563-module/include"
|
REQUIRES esp_timer app-module crypt-module gps-module lvgl-module lvgl-window-manager-module service-module
|
||||||
"Drivers/bmi270-module/include"
|
|
||||||
"Drivers/mpu6886-module/include"
|
|
||||||
"Drivers/pi4ioe5v6408-module/include"
|
|
||||||
"Drivers/qmi8658-module/include"
|
|
||||||
"Drivers/rx8130ce-module/include"
|
|
||||||
REQUIRES esp_timer
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Regular and core features
|
# Regular and core features
|
||||||
add_prebuilt_library(TactilityC Libraries/TactilityC/binary/libTactilityC.a)
|
add_prebuilt_library(TactilityC Libraries/TactilityC/binary/libTactilityC.a)
|
||||||
add_prebuilt_library(TactilityKernel Libraries/TactilityKernel/binary/libTactilityKernel.a)
|
add_prebuilt_library(TactilityKernel Libraries/TactilityKernel/binary/libTactilityKernel.a)
|
||||||
add_prebuilt_library(lvgl Libraries/lvgl/binary/liblvgl.a)
|
add_prebuilt_library(lvgl Libraries/lvgl/binary/liblvgl.a)
|
||||||
|
add_prebuilt_library(minmea Libraries/minmea/binary/libminmea.a)
|
||||||
|
add_prebuilt_library(minitar Libraries/minitar/binary/libminitar.a)
|
||||||
|
|
||||||
target_link_libraries(${COMPONENT_LIB} INTERFACE TactilityC)
|
target_link_libraries(${COMPONENT_LIB} INTERFACE TactilityC)
|
||||||
target_link_libraries(${COMPONENT_LIB} INTERFACE TactilityKernel)
|
target_link_libraries(${COMPONENT_LIB} INTERFACE TactilityKernel)
|
||||||
target_link_libraries(${COMPONENT_LIB} INTERFACE lvgl)
|
target_link_libraries(${COMPONENT_LIB} INTERFACE lvgl)
|
||||||
|
target_link_libraries(${COMPONENT_LIB} INTERFACE minmea)
|
||||||
|
target_link_libraries(${COMPONENT_LIB} INTERFACE minitar)
|
||||||
|
|||||||
@ -18,19 +18,18 @@ macro(tactility_project project_name)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(EXTRA_COMPONENT_DIRS
|
set(EXTRA_COMPONENT_DIRS
|
||||||
"Libraries/TactilityFreeRtos"
|
"${TACTILITY_SDK_PATH}/Libraries/TactilityFreeRtos"
|
||||||
"Modules"
|
"${TACTILITY_SDK_PATH}/Modules"
|
||||||
"Drivers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(COMPONENTS
|
set(COMPONENTS
|
||||||
TactilityFreeRtos
|
TactilityFreeRtos
|
||||||
bm8563-module
|
app-module
|
||||||
bmi270-module
|
crypt-module
|
||||||
mpu6886-module
|
gps-module
|
||||||
pi4ioe5v6408-module
|
lvgl-module
|
||||||
qmi8658-module
|
lvgl-window-manager-module
|
||||||
rx8130ce-module
|
service-module
|
||||||
)
|
)
|
||||||
|
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|||||||
135
Buildscripts/gh-download-artifacts.py
Executable file
135
Buildscripts/gh-download-artifacts.py
Executable file
@ -0,0 +1,135 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Download all artifacts from a GitHub Actions workflow run."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
API_URL = "https://api.github.com"
|
||||||
|
|
||||||
|
|
||||||
|
def get_token(cli_token: str | None) -> str:
|
||||||
|
if cli_token:
|
||||||
|
return cli_token
|
||||||
|
env_token = os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN")
|
||||||
|
if env_token:
|
||||||
|
return env_token
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["gh", "auth", "token"], capture_output=True, text=True, check=True
|
||||||
|
)
|
||||||
|
return result.stdout.strip()
|
||||||
|
except (FileNotFoundError, subprocess.CalledProcessError):
|
||||||
|
sys.exit(
|
||||||
|
"No token found. Pass --token, set GITHUB_TOKEN/GH_TOKEN, or run `gh auth login`."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_repo(cli_repo: str | None) -> str:
|
||||||
|
if cli_repo:
|
||||||
|
return cli_repo
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "remote", "get-url", "origin"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
url = result.stdout.strip()
|
||||||
|
except (FileNotFoundError, subprocess.CalledProcessError):
|
||||||
|
sys.exit("Could not detect repo from git remote. Pass --repo owner/name.")
|
||||||
|
|
||||||
|
# Handle both SSH and HTTPS remote URLs.
|
||||||
|
if url.startswith("git@"):
|
||||||
|
path = url.split(":", 1)[1]
|
||||||
|
else:
|
||||||
|
path = url.split("github.com/", 1)[-1]
|
||||||
|
return path.removesuffix(".git")
|
||||||
|
|
||||||
|
|
||||||
|
def download_artifacts(repo: str, run_id: str, token: str, out_dir: Path):
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {token}",
|
||||||
|
"Accept": "application/vnd.github+json",
|
||||||
|
"X-GitHub-Api-Version": "2022-11-28",
|
||||||
|
}
|
||||||
|
|
||||||
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
url = f"{API_URL}/repos/{repo}/actions/runs/{run_id}/artifacts"
|
||||||
|
artifacts = []
|
||||||
|
while url:
|
||||||
|
resp = requests.get(url, headers=headers, params={"per_page": 100})
|
||||||
|
resp.raise_for_status()
|
||||||
|
data = resp.json()
|
||||||
|
artifacts.extend(data["artifacts"])
|
||||||
|
url = resp.links.get("next", {}).get("url")
|
||||||
|
|
||||||
|
if not artifacts:
|
||||||
|
print(f"No artifacts found for run {run_id}.")
|
||||||
|
return
|
||||||
|
|
||||||
|
for artifact in artifacts:
|
||||||
|
name = artifact["name"]
|
||||||
|
if name == "all-artifacts":
|
||||||
|
print(f"Skipping {name}")
|
||||||
|
continue
|
||||||
|
if artifact.get("expired"):
|
||||||
|
print(f"Skipping expired artifact: {name}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f"Downloading {name} ({artifact['size_in_bytes']} bytes)...")
|
||||||
|
download_url = artifact["archive_download_url"]
|
||||||
|
resp = requests.get(download_url, headers=headers, stream=True)
|
||||||
|
resp.raise_for_status()
|
||||||
|
|
||||||
|
zip_path = out_dir / f"{name}.zip"
|
||||||
|
with open(zip_path, "wb") as f:
|
||||||
|
for chunk in resp.iter_content(chunk_size=8192):
|
||||||
|
f.write(chunk)
|
||||||
|
|
||||||
|
if name.endswith(".bin"):
|
||||||
|
with zipfile.ZipFile(zip_path) as zf:
|
||||||
|
zf.extractall(out_dir)
|
||||||
|
zip_path.unlink()
|
||||||
|
print(f" Extracted to {out_dir}")
|
||||||
|
else:
|
||||||
|
print(f" Saved to {zip_path}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("run_id", help="GitHub Actions run ID")
|
||||||
|
parser.add_argument(
|
||||||
|
"--repo",
|
||||||
|
help="owner/repo (default: detected from git remote 'origin')",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--token",
|
||||||
|
help="GitHub token (default: $GITHUB_TOKEN, $GH_TOKEN, or `gh auth token`)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--out",
|
||||||
|
default="artifacts",
|
||||||
|
help="Output directory (default: ./artifacts)",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
repo = get_repo(args.repo)
|
||||||
|
token = get_token(args.token)
|
||||||
|
out_dir = Path(args.out)
|
||||||
|
|
||||||
|
print(f"Repo: {repo}")
|
||||||
|
print(f"Run ID: {args.run_id}")
|
||||||
|
print(f"Output: {out_dir.resolve()}")
|
||||||
|
|
||||||
|
download_artifacts(repo, args.run_id, token, out_dir)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
57
Buildscripts/gh-generate-device-matrix.py
Normal file
57
Buildscripts/gh-generate-device-matrix.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import fnmatch
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
DEVICES_DIRECTORY = os.path.join(os.path.dirname(__file__), "..", "Devices")
|
||||||
|
|
||||||
|
IGNORE_LIST = {
|
||||||
|
"simulator",
|
||||||
|
"generic-*",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def is_ignored(device_id):
|
||||||
|
return any(fnmatch.fnmatch(device_id, pattern) for pattern in IGNORE_LIST)
|
||||||
|
|
||||||
|
def read_properties_file(path):
|
||||||
|
properties = {}
|
||||||
|
with open(path, "r") as file:
|
||||||
|
for line in file:
|
||||||
|
stripped = line.strip()
|
||||||
|
if not stripped or stripped.startswith("#"):
|
||||||
|
continue
|
||||||
|
key, sep, value = line.partition("=")
|
||||||
|
if not sep:
|
||||||
|
continue
|
||||||
|
properties[key.strip()] = value.strip()
|
||||||
|
return properties
|
||||||
|
|
||||||
|
def get_board_entries():
|
||||||
|
boards = []
|
||||||
|
for device_id in sorted(os.listdir(DEVICES_DIRECTORY)):
|
||||||
|
if is_ignored(device_id):
|
||||||
|
continue
|
||||||
|
device_dir = os.path.join(DEVICES_DIRECTORY, device_id)
|
||||||
|
properties_path = os.path.join(device_dir, "device.properties")
|
||||||
|
if not os.path.isdir(device_dir) or not os.path.isfile(properties_path):
|
||||||
|
continue
|
||||||
|
properties = read_properties_file(properties_path)
|
||||||
|
target = properties.get("hardware.target")
|
||||||
|
if not target:
|
||||||
|
sys.exit(f"ERROR: {properties_path} has no hardware.target property")
|
||||||
|
boards.append({"id": device_id, "arch": target.lower()})
|
||||||
|
return boards
|
||||||
|
|
||||||
|
def main():
|
||||||
|
matrix_json = json.dumps({"board": get_board_entries()})
|
||||||
|
|
||||||
|
github_output = os.environ.get("GITHUB_OUTPUT")
|
||||||
|
if github_output:
|
||||||
|
with open(github_output, "a") as file:
|
||||||
|
file.write(f"matrix={matrix_json}\n")
|
||||||
|
else:
|
||||||
|
print(matrix_json)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
126
Buildscripts/gh-release-summary.py
Executable file
126
Buildscripts/gh-release-summary.py
Executable file
@ -0,0 +1,126 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Summarize commits between the last 2 pushed git tags into commits.md and pull-requests.md."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
PR_REF_RE = re.compile(r"#(\d+)")
|
||||||
|
|
||||||
|
|
||||||
|
def get_repo(cli_repo: str | None) -> str:
|
||||||
|
if cli_repo:
|
||||||
|
return cli_repo
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "remote", "get-url", "origin"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
url = result.stdout.strip()
|
||||||
|
except (FileNotFoundError, subprocess.CalledProcessError):
|
||||||
|
sys.exit("Could not detect repo from git remote. Pass --repo owner/name.")
|
||||||
|
|
||||||
|
if url.startswith("git@"):
|
||||||
|
path = url.split(":", 1)[1]
|
||||||
|
else:
|
||||||
|
path = url.split("github.com/", 1)[-1]
|
||||||
|
return path.removesuffix(".git")
|
||||||
|
|
||||||
|
|
||||||
|
def get_last_two_tags() -> tuple[str, str]:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "tag", "--sort=-creatordate"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
tags = [t for t in result.stdout.splitlines() if t]
|
||||||
|
if len(tags) < 2:
|
||||||
|
sys.exit(f"Need at least 2 tags, found {len(tags)}.")
|
||||||
|
return tags[1], tags[0] # previous, latest
|
||||||
|
|
||||||
|
|
||||||
|
def get_commits(prev_tag: str, latest_tag: str) -> list[tuple[str, str]]:
|
||||||
|
result = subprocess.run(
|
||||||
|
[
|
||||||
|
"git",
|
||||||
|
"log",
|
||||||
|
"--pretty=format:%H\x1f%s",
|
||||||
|
f"{prev_tag}..{latest_tag}",
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
commits = []
|
||||||
|
for line in result.stdout.splitlines():
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
sha, subject = line.split("\x1f", 1)
|
||||||
|
commits.append((sha, subject))
|
||||||
|
return commits
|
||||||
|
|
||||||
|
|
||||||
|
def write_commits_md(path: Path, repo: str, prev_tag: str, latest_tag: str, commits: list[tuple[str, str]]):
|
||||||
|
lines = [f"# Commits: {prev_tag}..{latest_tag}", ""]
|
||||||
|
for sha, subject in commits:
|
||||||
|
link = f"https://github.com/{repo}/commit/{sha}"
|
||||||
|
lines.append(f"- [{subject}]({link})")
|
||||||
|
path.write_text("\n".join(lines) + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
def write_pull_requests_md(path: Path, repo: str, commits: list[tuple[str, str]]):
|
||||||
|
pr_numbers = []
|
||||||
|
seen = set()
|
||||||
|
for _, subject in commits:
|
||||||
|
for match in PR_REF_RE.finditer(subject):
|
||||||
|
number = match.group(1)
|
||||||
|
if number not in seen:
|
||||||
|
seen.add(number)
|
||||||
|
pr_numbers.append(number)
|
||||||
|
|
||||||
|
lines = ["# Pull Requests", ""]
|
||||||
|
for number in pr_numbers:
|
||||||
|
lines.append(f"- https://github.com/{repo}/pull/{number}")
|
||||||
|
path.write_text("\n".join(lines) + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument(
|
||||||
|
"--repo",
|
||||||
|
help="owner/repo (default: detected from git remote 'origin')",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--out",
|
||||||
|
default=".",
|
||||||
|
help="Output directory for commits.md and pull-requests.md (default: current directory)",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
repo = get_repo(args.repo)
|
||||||
|
prev_tag, latest_tag = get_last_two_tags()
|
||||||
|
commits = get_commits(prev_tag, latest_tag)
|
||||||
|
|
||||||
|
out_dir = Path(args.out)
|
||||||
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
commits_path = out_dir / "release-commits.md"
|
||||||
|
prs_path = out_dir / "release-pull-requests.md"
|
||||||
|
|
||||||
|
write_commits_md(commits_path, repo, prev_tag, latest_tag, commits)
|
||||||
|
write_pull_requests_md(prs_path, repo, commits)
|
||||||
|
|
||||||
|
print(f"Repo: {repo}")
|
||||||
|
print(f"Tags: {prev_tag}..{latest_tag}")
|
||||||
|
print(f"Commits: {len(commits)}")
|
||||||
|
print(f"Wrote {commits_path}")
|
||||||
|
print(f"Wrote {prs_path}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -1,4 +1,11 @@
|
|||||||
function(GET_PROPERTY_VALUE PROPERTIES_CONTENT_VAR KEY_NAME RESULT_VAR)
|
function(GET_PROPERTY_VALUE PROPERTIES_CONTENT_VAR KEY_NAME RESULT_VAR)
|
||||||
|
# Optional 4th arg: default value returned when key is missing, instead of erroring.
|
||||||
|
set(has_default FALSE)
|
||||||
|
if (${ARGC} GREATER 3)
|
||||||
|
set(has_default TRUE)
|
||||||
|
set(default_value "${ARGV3}")
|
||||||
|
endif ()
|
||||||
|
|
||||||
# Search for the key and its value in the properties content
|
# Search for the key and its value in the properties content
|
||||||
# Supports KEY=VALUE, KEY="VALUE", and optional spaces around =
|
# Supports KEY=VALUE, KEY="VALUE", and optional spaces around =
|
||||||
# Use parentheses to capture the value
|
# Use parentheses to capture the value
|
||||||
@ -6,6 +13,8 @@ function(GET_PROPERTY_VALUE PROPERTIES_CONTENT_VAR KEY_NAME RESULT_VAR)
|
|||||||
# So we look for the key at the beginning of the string or after a newline.
|
# So we look for the key at the beginning of the string or after a newline.
|
||||||
if ("${${PROPERTIES_CONTENT_VAR}}" MATCHES "(^|\n)${KEY_NAME}[ \t]*=[ \t]*\"?([^\n\"]*)\"?")
|
if ("${${PROPERTIES_CONTENT_VAR}}" MATCHES "(^|\n)${KEY_NAME}[ \t]*=[ \t]*\"?([^\n\"]*)\"?")
|
||||||
set(${RESULT_VAR} "${CMAKE_MATCH_2}" PARENT_SCOPE)
|
set(${RESULT_VAR} "${CMAKE_MATCH_2}" PARENT_SCOPE)
|
||||||
|
elseif (has_default)
|
||||||
|
set(${RESULT_VAR} "${default_value}" PARENT_SCOPE)
|
||||||
else ()
|
else ()
|
||||||
message(FATAL_ERROR "Property '${KEY_NAME}' not found in the properties content.")
|
message(FATAL_ERROR "Property '${KEY_NAME}' not found in the properties content.")
|
||||||
endif ()
|
endif ()
|
||||||
@ -28,7 +37,6 @@ function(READ_PROPERTIES_TO_MAP PROPERTY_FILE RESULT_VAR)
|
|||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
file(STRINGS ${PROPERTY_FILE_ABS} lines)
|
file(STRINGS ${PROPERTY_FILE_ABS} lines)
|
||||||
set(current_section "")
|
|
||||||
set(map_content "")
|
set(map_content "")
|
||||||
|
|
||||||
foreach(line IN LISTS lines)
|
foreach(line IN LISTS lines)
|
||||||
@ -37,9 +45,7 @@ function(READ_PROPERTIES_TO_MAP PROPERTY_FILE RESULT_VAR)
|
|||||||
continue()
|
continue()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (line MATCHES "^\\[.*\\]$")
|
if (line MATCHES "^([^=]+)=(.*)$")
|
||||||
set(current_section "${line}")
|
|
||||||
elseif (line MATCHES "^([^=]+)=(.*)$")
|
|
||||||
set(key "${CMAKE_MATCH_1}")
|
set(key "${CMAKE_MATCH_1}")
|
||||||
set(value "${CMAKE_MATCH_2}")
|
set(value "${CMAKE_MATCH_2}")
|
||||||
string(STRIP "${key}" key)
|
string(STRIP "${key}" key)
|
||||||
@ -49,7 +55,7 @@ function(READ_PROPERTIES_TO_MAP PROPERTY_FILE RESULT_VAR)
|
|||||||
set(value "${CMAKE_MATCH_1}")
|
set(value "${CMAKE_MATCH_1}")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
list(APPEND map_content "${current_section}${key}" "${value}")
|
list(APPEND map_content "${key}" "${value}")
|
||||||
endif ()
|
endif ()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
|||||||
@ -88,6 +88,17 @@ def write_module_cmakelists(path, content):
|
|||||||
with open(path, 'w') as f:
|
with open(path, 'w') as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
|
|
||||||
|
def driver_is_available(driver_name):
|
||||||
|
"""
|
||||||
|
Some drivers only build for certain chip targets (e.g. sc2356-module is ESP32-P4 only,
|
||||||
|
since it depends on esp_video/esp_cam_sensor/PPA which are themselves chip-restricted).
|
||||||
|
Build output presence is the single source of truth for "does this driver support the
|
||||||
|
current target" - no separate manifest to keep in sync with the real CMakeLists.txt
|
||||||
|
REQUIRES/Kconfig guards.
|
||||||
|
"""
|
||||||
|
binary_pattern = f'build/esp-idf/{driver_name}/lib{driver_name}.a'
|
||||||
|
return bool(glob.glob(binary_pattern))
|
||||||
|
|
||||||
def add_driver(target_path, driver_name):
|
def add_driver(target_path, driver_name):
|
||||||
mappings = get_driver_mappings(driver_name)
|
mappings = get_driver_mappings(driver_name)
|
||||||
map_copy(mappings, target_path)
|
map_copy(mappings, target_path)
|
||||||
@ -100,6 +111,14 @@ def add_module(target_path, module_name):
|
|||||||
cmakelists_content = create_module_cmakelists(module_name)
|
cmakelists_content = create_module_cmakelists(module_name)
|
||||||
write_module_cmakelists(os.path.join(target_path, f"Modules/{module_name}/CMakeLists.txt"), cmakelists_content)
|
write_module_cmakelists(os.path.join(target_path, f"Modules/{module_name}/CMakeLists.txt"), cmakelists_content)
|
||||||
|
|
||||||
|
def generate_tactility_sdk_cmake(target_path):
|
||||||
|
src = os.path.join('Buildscripts', 'TactilitySDK', 'TactilitySDK.cmake')
|
||||||
|
shutil.copy2(src, os.path.join(target_path, 'TactilitySDK.cmake'))
|
||||||
|
|
||||||
|
def generate_tactility_sdk_top_cmakelists(target_path):
|
||||||
|
src = os.path.join('Buildscripts', 'TactilitySDK', 'CMakeLists.txt')
|
||||||
|
shutil.copy2(src, os.path.join(target_path, 'CMakeLists.txt'))
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print("Usage: release-sdk.py [target_path]")
|
print("Usage: release-sdk.py [target_path]")
|
||||||
@ -118,7 +137,7 @@ def main():
|
|||||||
{'src': 'TactilityC/CMakeLists.txt', 'dst': 'Libraries/TactilityC/'},
|
{'src': 'TactilityC/CMakeLists.txt', 'dst': 'Libraries/TactilityC/'},
|
||||||
{'src': 'TactilityC/LICENSE*.*', 'dst': 'Libraries/TactilityC/'},
|
{'src': 'TactilityC/LICENSE*.*', 'dst': 'Libraries/TactilityC/'},
|
||||||
# TactilityFreeRtos
|
# TactilityFreeRtos
|
||||||
{'src': 'TactilityFreeRtos/Include/**', 'dst': 'Libraries/TactilityFreeRtos/include/'},
|
{'src': 'TactilityFreeRtos/Include/**', 'dst': 'Libraries/TactilityFreeRtos/Include/'},
|
||||||
{'src': 'TactilityFreeRtos/CMakeLists.txt', 'dst': 'Libraries/TactilityFreeRtos/'},
|
{'src': 'TactilityFreeRtos/CMakeLists.txt', 'dst': 'Libraries/TactilityFreeRtos/'},
|
||||||
{'src': 'TactilityFreeRtos/LICENSE*.*', 'dst': 'Libraries/TactilityFreeRtos/'},
|
{'src': 'TactilityFreeRtos/LICENSE*.*', 'dst': 'Libraries/TactilityFreeRtos/'},
|
||||||
# TactilityKernel
|
# TactilityKernel
|
||||||
@ -136,23 +155,32 @@ def main():
|
|||||||
# elf_loader
|
# elf_loader
|
||||||
{'src': 'Libraries/elf_loader/elf_loader.cmake', 'dst': 'Libraries/elf_loader/'},
|
{'src': 'Libraries/elf_loader/elf_loader.cmake', 'dst': 'Libraries/elf_loader/'},
|
||||||
{'src': 'Libraries/elf_loader/license.txt', 'dst': 'Libraries/elf_loader/'},
|
{'src': 'Libraries/elf_loader/license.txt', 'dst': 'Libraries/elf_loader/'},
|
||||||
# Final scripts
|
# minitar
|
||||||
{'src': 'Buildscripts/TactilitySDK/TactilitySDK.cmake', 'dst': ''},
|
{'src': 'build/esp-idf/minitar/libminitar.a', 'dst': 'Libraries/minitar/binary/'},
|
||||||
{'src': 'Buildscripts/TactilitySDK/CMakeLists.txt', 'dst': ''},
|
{'src': 'Libraries/minitar/minitar/minitar.h', 'dst': 'Libraries/minitar/include/'},
|
||||||
|
{'src': 'Libraries/minitar/minitar/LICENSE*', 'dst': 'Libraries/minitar/'},
|
||||||
|
# minmea
|
||||||
|
{'src': 'build/esp-idf/minmea/libminmea.a', 'dst': 'Libraries/minmea/binary/'},
|
||||||
|
{'src': 'Libraries/minmea/Include/**', 'dst': 'Libraries/minmea/include/'},
|
||||||
|
{'src': 'Libraries/minmea/CMakeLists.txt', 'dst': 'Libraries/minmea/'},
|
||||||
|
{'src': 'Libraries/minmea/README.md', 'dst': 'Libraries/minmea/'},
|
||||||
|
{'src': 'Libraries/minmea/LICENSE*.*', 'dst': 'Libraries/minmea/'},
|
||||||
|
{'src': 'Libraries/minmea/COPYING', 'dst': 'Libraries/minmea/'},
|
||||||
]
|
]
|
||||||
|
|
||||||
map_copy(mappings, target_path)
|
map_copy(mappings, target_path)
|
||||||
|
|
||||||
# Modules
|
# Modules
|
||||||
|
add_module(target_path, "app-module")
|
||||||
|
add_module(target_path, "crypt-module")
|
||||||
|
add_module(target_path, "gps-module")
|
||||||
add_module(target_path, "lvgl-module")
|
add_module(target_path, "lvgl-module")
|
||||||
|
add_module(target_path, "lvgl-window-manager-module")
|
||||||
|
add_module(target_path, "service-module")
|
||||||
|
|
||||||
# Drivers
|
# Final scripts - copied verbatim
|
||||||
add_driver(target_path, "bm8563-module")
|
generate_tactility_sdk_cmake(target_path)
|
||||||
add_driver(target_path, "bmi270-module")
|
generate_tactility_sdk_top_cmakelists(target_path)
|
||||||
add_driver(target_path, "mpu6886-module")
|
|
||||||
add_driver(target_path, "pi4ioe5v6408-module")
|
|
||||||
add_driver(target_path, "qmi8658-module")
|
|
||||||
add_driver(target_path, "rx8130ce-module")
|
|
||||||
|
|
||||||
# Output ESP-IDF SDK version to file
|
# Output ESP-IDF SDK version to file
|
||||||
esp_idf_version = os.environ.get("ESP_IDF_VERSION", "")
|
esp_idf_version = os.environ.get("ESP_IDF_VERSION", "")
|
||||||
|
|||||||
84
Buildscripts/release-summary.py
Executable file
84
Buildscripts/release-summary.py
Executable file
@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate a release summary markdown by combining gh-release-summary.py output with Claude."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
SECTIONS = [
|
||||||
|
"New Devices",
|
||||||
|
"New Features",
|
||||||
|
"New Apps",
|
||||||
|
"Improvements & Fixes",
|
||||||
|
"Other Changes",
|
||||||
|
"TactilitySDK",
|
||||||
|
"Known Issues",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def run_gh_release_summary(out_dir: Path, repo: str | None):
|
||||||
|
cmd = [sys.executable, str(SCRIPT_DIR / "gh-release-summary.py"), "--out", str(out_dir)]
|
||||||
|
if repo:
|
||||||
|
cmd += ["--repo", repo]
|
||||||
|
subprocess.run(cmd, check=True)
|
||||||
|
|
||||||
|
|
||||||
|
def build_prompt() -> str:
|
||||||
|
section_list = "\n".join(f"- {s}" for s in SECTIONS)
|
||||||
|
return (
|
||||||
|
"You are given a list of commits (with GitHub commit links) and a list of pull "
|
||||||
|
"request links from a software release. Write a release summary in Markdown, "
|
||||||
|
"organizing changes into these sections (omit a section if it has no relevant "
|
||||||
|
"changes):\n\n"
|
||||||
|
f"{section_list}\n\n"
|
||||||
|
"Use concise bullet points. Base every bullet strictly on the given commits and "
|
||||||
|
"pull requests, referencing the pull request link where applicable. Output only "
|
||||||
|
"the Markdown, no commentary before or after it."
|
||||||
|
"If a pull request or commit has multiple changes add them as separate entries."
|
||||||
|
"Ignore irrelevant changes: documentation, minor fixes, non-descript fixes, scripts, github workflows."
|
||||||
|
"For 'New Devices': only mention the device name, don't like pull request."
|
||||||
|
"For 'Improvements & Fixes': Avoid non-descript entries, write specifics."
|
||||||
|
"For 'TactilitySDK': Avoid generic changes, be specific. Mention API changes."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def run_claude(commits_md: str, prs_md: str) -> str:
|
||||||
|
prompt = build_prompt()
|
||||||
|
stdin_content = f"## Commits\n\n{commits_md}\n\n## Pull Requests\n\n{prs_md}"
|
||||||
|
result = subprocess.run(
|
||||||
|
["claude", "-p", prompt],
|
||||||
|
input=stdin_content,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
return result.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("--repo", help="owner/repo (default: detected from git remote 'origin')")
|
||||||
|
parser.add_argument("--out", default=".", help="Output directory (default: current directory)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
out_dir = Path(args.out)
|
||||||
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
run_gh_release_summary(out_dir, args.repo)
|
||||||
|
|
||||||
|
commits_md = (out_dir / "release-commits.md").read_text()
|
||||||
|
prs_md = (out_dir / "release-pull-requests.md").read_text()
|
||||||
|
|
||||||
|
print("Generating release summary via claude...")
|
||||||
|
summary = run_claude(commits_md, prs_md)
|
||||||
|
|
||||||
|
summary_path = out_dir / "release-summary.md"
|
||||||
|
summary_path.write_text(summary)
|
||||||
|
print(f"Wrote {summary_path}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -1,6 +1,6 @@
|
|||||||
# Increase stack size for Wi-Fi (fixes crash after scan)
|
# Increase stack size for Wi-Fi (fixes crash after scan)
|
||||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3072
|
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3072
|
||||||
# Ensure large enough stack for network operations
|
# Ensure large enough stack for network operations (e.g. AppHub)
|
||||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=6144
|
CONFIG_ESP_MAIN_TASK_STACK_SIZE=6144
|
||||||
# Fixes static assertion: FLASH and PSRAM Mode configuration are not supported
|
# Fixes static assertion: FLASH and PSRAM Mode configuration are not supported
|
||||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
||||||
@ -26,6 +26,9 @@ CONFIG_LV_USE_WIN=n
|
|||||||
CONFIG_LV_USE_SNAPSHOT=y
|
CONFIG_LV_USE_SNAPSHOT=y
|
||||||
CONFIG_LV_BUILD_EXAMPLES=n
|
CONFIG_LV_BUILD_EXAMPLES=n
|
||||||
CONFIG_LV_BUILD_DEMOS=n
|
CONFIG_LV_BUILD_DEMOS=n
|
||||||
|
# Slot 0 is reserved by ESP-IDF's pthread API (see esp_wifi/lwip pthread_getspecific usage).
|
||||||
|
# Tactility's Thread wrappers use slot 1 for their own self-pointer.
|
||||||
|
CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=2
|
||||||
CONFIG_FREERTOS_HZ=1000
|
CONFIG_FREERTOS_HZ=1000
|
||||||
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2
|
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2
|
||||||
CONFIG_FREERTOS_SMP=n
|
CONFIG_FREERTOS_SMP=n
|
||||||
@ -41,6 +44,11 @@ CONFIG_WL_SECTOR_MODE_SAFE=y
|
|||||||
CONFIG_WL_SECTOR_MODE=1
|
CONFIG_WL_SECTOR_MODE=1
|
||||||
# Allow new i2c_master API (used by Tab5Keyboard for LP_I2C_NUM_0) to coexist with legacy i2c driver
|
# Allow new i2c_master API (used by Tab5Keyboard for LP_I2C_NUM_0) to coexist with legacy i2c driver
|
||||||
CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK=y
|
CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK=y
|
||||||
|
# Allow new adc_oneshot API (esp32_adc_oneshot kernel driver, linked into every device via
|
||||||
|
# platform-esp32) to coexist with boards still reading battery voltage via the legacy ADC driver
|
||||||
|
# (e.g. m5stack-cardputer, m5stack-cardputer-adv, lilygo-thmi). Only one API is ever actually used
|
||||||
|
# per board at runtime, so this just bypasses ESP-IDF's link-time-only conflict abort.
|
||||||
|
CONFIG_ADC_SKIP_LEGACY_CONFLICT_CHECK=y
|
||||||
# Wi-Fi memory optimizations
|
# Wi-Fi memory optimizations
|
||||||
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=3
|
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=3
|
||||||
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=5
|
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=5
|
||||||
|
|||||||
@ -41,11 +41,12 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
|||||||
"Modules"
|
"Modules"
|
||||||
"Platforms/platform-esp32"
|
"Platforms/platform-esp32"
|
||||||
"TactilityKernel"
|
"TactilityKernel"
|
||||||
|
"TactilityKernelCpp"
|
||||||
"Tactility"
|
"Tactility"
|
||||||
"TactilityC"
|
"TactilityC"
|
||||||
"TactilityCore"
|
|
||||||
"TactilityFreeRtos"
|
"TactilityFreeRtos"
|
||||||
"Libraries/elf_loader"
|
"Libraries/elf_loader"
|
||||||
|
"Libraries/esp_epaper"
|
||||||
"Libraries/lv_screenshot"
|
"Libraries/lv_screenshot"
|
||||||
"Libraries/minitar"
|
"Libraries/minitar"
|
||||||
"Libraries/minmea"
|
"Libraries/minmea"
|
||||||
@ -70,12 +71,16 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
|||||||
|
|
||||||
else ()
|
else ()
|
||||||
message("Building for sim target")
|
message("Building for sim target")
|
||||||
|
# Devices/simulator/Source/Simulator.cpp always defines its own hardwareConfiguration; without
|
||||||
|
# this, Firmware/Source/Main.cpp's #else branch also defines an empty one (its ESP32-only
|
||||||
|
# fallback for devices migrated off the deprecated HAL), causing a duplicate-definition link error.
|
||||||
add_compile_definitions(CONFIG_TT_DEVICE_ID="simulator")
|
add_compile_definitions(CONFIG_TT_DEVICE_ID="simulator")
|
||||||
add_compile_definitions(CONFIG_TT_DEVICE_NAME="Simulator")
|
add_compile_definitions(CONFIG_TT_DEVICE_NAME="Simulator")
|
||||||
add_compile_definitions(CONFIG_TT_DEVICE_VENDOR="")
|
add_compile_definitions(CONFIG_TT_DEVICE_VENDOR="")
|
||||||
add_compile_definitions(CONFIG_TT_DEVICE_NAME_SIMPLE="Simulator")
|
add_compile_definitions(CONFIG_TT_DEVICE_NAME_SIMPLE="Simulator")
|
||||||
add_compile_definitions(CONFIG_TT_LAUNCHER_APP_ID="Launcher")
|
add_compile_definitions(CONFIG_TT_LAUNCHER_APP_ID="tactility.launcher")
|
||||||
add_compile_definitions(CONFIG_TT_AUTO_START_APP_ID="")
|
add_compile_definitions(CONFIG_TT_AUTO_START_APP_ID="")
|
||||||
|
add_compile_definitions(CONFIG_TT_USER_DATA_LOCATION_INTERNAL)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
project(Tactility)
|
project(Tactility)
|
||||||
@ -83,9 +88,9 @@ project(Tactility)
|
|||||||
# Defined as regular project for PC and component for ESP
|
# Defined as regular project for PC and component for ESP
|
||||||
if (NOT DEFINED ENV{ESP_IDF_VERSION})
|
if (NOT DEFINED ENV{ESP_IDF_VERSION})
|
||||||
add_subdirectory(Tactility)
|
add_subdirectory(Tactility)
|
||||||
add_subdirectory(TactilityCore)
|
|
||||||
add_subdirectory(TactilityFreeRtos)
|
add_subdirectory(TactilityFreeRtos)
|
||||||
add_subdirectory(TactilityKernel)
|
add_subdirectory(TactilityKernel)
|
||||||
|
add_subdirectory(TactilityKernelCpp)
|
||||||
add_subdirectory(Platforms/platform-posix)
|
add_subdirectory(Platforms/platform-posix)
|
||||||
add_subdirectory(Devices/simulator)
|
add_subdirectory(Devices/simulator)
|
||||||
add_subdirectory(Libraries/cJSON)
|
add_subdirectory(Libraries/cJSON)
|
||||||
@ -93,8 +98,14 @@ if (NOT DEFINED ENV{ESP_IDF_VERSION})
|
|||||||
add_subdirectory(Libraries/QRCode)
|
add_subdirectory(Libraries/QRCode)
|
||||||
add_subdirectory(Libraries/minitar)
|
add_subdirectory(Libraries/minitar)
|
||||||
add_subdirectory(Libraries/minmea)
|
add_subdirectory(Libraries/minmea)
|
||||||
add_subdirectory(Modules/hal-device-module)
|
|
||||||
add_subdirectory(Modules/lvgl-module)
|
add_subdirectory(Modules/lvgl-module)
|
||||||
|
add_subdirectory(Modules/crypt-module)
|
||||||
|
add_subdirectory(Modules/gps-module)
|
||||||
|
add_subdirectory(Modules/service-module)
|
||||||
|
add_subdirectory(Modules/app-module)
|
||||||
|
add_subdirectory(Modules/lvgl-window-manager-module)
|
||||||
|
add_subdirectory(Drivers/gps-generic-module)
|
||||||
|
add_subdirectory(Drivers/gps-meshtastic-module)
|
||||||
|
|
||||||
# FreeRTOS
|
# FreeRTOS
|
||||||
set(FREERTOS_CONFIG_FILE_DIRECTORY ${PROJECT_SOURCE_DIR}/Devices/simulator/Source CACHE STRING "")
|
set(FREERTOS_CONFIG_FILE_DIRECTORY ${PROJECT_SOURCE_DIR}/Devices/simulator/Source CACHE STRING "")
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
enableOnBoot=false
|
|
||||||
@ -1 +0,0 @@
|
|||||||
enableOnBoot=false
|
|
||||||
@ -1 +0,0 @@
|
|||||||
enableOnBoot=false
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
language=en-US
|
|
||||||
timeFormat24h=true
|
|
||||||
dateFormat=DD/MM/YYYY
|
|
||||||
region=EU
|
|
||||||
timezone=Europe/Amsterdam
|
|
||||||
@ -485,7 +485,7 @@
|
|||||||
<button class="btn btn-secondary btn-sm" onclick="goUp()" id="upBtn">Up</button>
|
<button class="btn btn-secondary btn-sm" onclick="goUp()" id="upBtn">Up</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar-sep"></div>
|
<div class="toolbar-sep"></div>
|
||||||
<input type="text" id="pathInput" value="/data" onkeydown="if(event.key==='Enter')refreshFiles()">
|
<input type="text" id="pathInput" value="" onkeydown="if(event.key==='Enter')refreshFiles()">
|
||||||
<button class="btn btn-primary btn-sm" onclick="refreshFiles()">Go</button>
|
<button class="btn btn-primary btn-sm" onclick="refreshFiles()">Go</button>
|
||||||
<div class="toolbar-sep"></div>
|
<div class="toolbar-sep"></div>
|
||||||
<div class="toolbar-group">
|
<div class="toolbar-group">
|
||||||
@ -739,7 +739,6 @@ function renderDashboard(data) {
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<h2>Quick Actions</h2>
|
<h2>Quick Actions</h2>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button class="btn btn-primary" onclick="syncAssets(this)">Sync Assets</button>
|
|
||||||
${data.features_enabled?.screenshot ? '<button class="btn btn-secondary" onclick="captureScreenshot(this)">Screenshot</button>' : ''}
|
${data.features_enabled?.screenshot ? '<button class="btn btn-secondary" onclick="captureScreenshot(this)">Screenshot</button>' : ''}
|
||||||
<button class="btn btn-danger" onclick="rebootDevice(this)">Reboot</button>
|
<button class="btn btn-danger" onclick="rebootDevice(this)">Reboot</button>
|
||||||
</div>
|
</div>
|
||||||
@ -1,63 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Tactility Dashboard</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 20px auto;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
color: #333;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.placeholder {
|
|
||||||
background: white;
|
|
||||||
padding: 40px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
||||||
text-align: center;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
.placeholder h2 {
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
.placeholder p {
|
|
||||||
color: #666;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #007bff;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Tactility Default Dashboard</h1>
|
|
||||||
|
|
||||||
<div class="placeholder">
|
|
||||||
<h2>Version 0 - Default Placeholder</h2>
|
|
||||||
<p>This is the default dashboard bundled with firmware.</p>
|
|
||||||
<p>To customize this interface:</p>
|
|
||||||
<ol style="text-align: left; display: inline-block;">
|
|
||||||
<li>Create your custom dashboard HTML/CSS/JS files</li>
|
|
||||||
<li>Add them to <code>/sdcard/tactility/webserver/</code></li>
|
|
||||||
<li>Create <code>version.json</code> with <code>{"version": 1}</code> or higher</li>
|
|
||||||
<li>Reboot or click "Sync Assets" on the <a href="/">Core Interface</a></li>
|
|
||||||
</ol>
|
|
||||||
<p><strong>Your custom assets will automatically replace this page!</strong></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="placeholder">
|
|
||||||
<p><a href="/">← Back to Core Interface</a></p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 0
|
|
||||||
}
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS ${SOURCE_FILES}
|
SRCS ${SOURCE_FILES}
|
||||||
INCLUDE_DIRS "Source"
|
INCLUDE_DIRS "source"
|
||||||
REQUIRES Tactility esp_lvgl_port esp_lcd RgbDisplay GT911 PwmBacklight driver vfs fatfs
|
REQUIRES TactilityKernel
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,31 +0,0 @@
|
|||||||
#include <PwmBacklight.h>
|
|
||||||
#include "devices/Display.h"
|
|
||||||
|
|
||||||
#include <Tactility/hal/Configuration.h>
|
|
||||||
#include <driver/gpio.h>
|
|
||||||
#include <freertos/FreeRTOS.h>
|
|
||||||
#include <freertos/task.h>
|
|
||||||
|
|
||||||
using namespace tt::hal;
|
|
||||||
|
|
||||||
static bool initBoot() {
|
|
||||||
//Display Reset
|
|
||||||
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_46, GPIO_MODE_OUTPUT));
|
|
||||||
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_46, 0));
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(100));
|
|
||||||
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_46, 1));
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
|
|
||||||
return driver::pwmbacklight::init(GPIO_NUM_21);
|
|
||||||
}
|
|
||||||
|
|
||||||
static DeviceVector createDevices() {
|
|
||||||
return {
|
|
||||||
createDisplay()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extern const Configuration hardwareConfiguration = {
|
|
||||||
.initBoot = initBoot,
|
|
||||||
.createDevices = createDevices
|
|
||||||
};
|
|
||||||
@ -1,107 +0,0 @@
|
|||||||
#include "Display.h"
|
|
||||||
|
|
||||||
#include <Gt911Touch.h>
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
#include <RgbDisplay.h>
|
|
||||||
#include <tactility/check.h>
|
|
||||||
#include <tactility/device.h>
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
|
|
||||||
// Note for future changes: Reset pin is 41 and interrupt pin is 40
|
|
||||||
auto* i2c = device_find_by_name("i2c0");
|
|
||||||
check(i2c);
|
|
||||||
auto configuration = std::make_unique<Gt911Touch::Configuration>(
|
|
||||||
i2c,
|
|
||||||
800,
|
|
||||||
480
|
|
||||||
);
|
|
||||||
|
|
||||||
return std::make_shared<Gt911Touch>(std::move(configuration));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
|
||||||
auto touch = createTouch();
|
|
||||||
|
|
||||||
constexpr uint32_t bufferPixels = 800 * 10;
|
|
||||||
|
|
||||||
esp_lcd_rgb_panel_config_t rgb_panel_config = {
|
|
||||||
.clk_src = LCD_CLK_SRC_DEFAULT,
|
|
||||||
.timings = {
|
|
||||||
.pclk_hz = 14000000,
|
|
||||||
.h_res = 800,
|
|
||||||
.v_res = 480,
|
|
||||||
.hsync_pulse_width = 4,
|
|
||||||
.hsync_back_porch = 8,
|
|
||||||
.hsync_front_porch = 8,
|
|
||||||
.vsync_pulse_width = 4,
|
|
||||||
.vsync_back_porch = 16,
|
|
||||||
.vsync_front_porch = 16,
|
|
||||||
.flags = {
|
|
||||||
.hsync_idle_low = false,
|
|
||||||
.vsync_idle_low = false,
|
|
||||||
.de_idle_high = false,
|
|
||||||
.pclk_active_neg = true,
|
|
||||||
.pclk_idle_high = true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.data_width = 16,
|
|
||||||
.bits_per_pixel = 0,
|
|
||||||
.num_fbs = 2,
|
|
||||||
.bounce_buffer_size_px = bufferPixels,
|
|
||||||
.sram_trans_align = 8,
|
|
||||||
.psram_trans_align = 64,
|
|
||||||
.hsync_gpio_num = GPIO_NUM_NC,
|
|
||||||
.vsync_gpio_num = GPIO_NUM_NC,
|
|
||||||
.de_gpio_num = GPIO_NUM_38,
|
|
||||||
.pclk_gpio_num = GPIO_NUM_5,
|
|
||||||
.disp_gpio_num = GPIO_NUM_NC,
|
|
||||||
.data_gpio_nums = {
|
|
||||||
GPIO_NUM_17, // B
|
|
||||||
GPIO_NUM_18, // B
|
|
||||||
GPIO_NUM_48, // B
|
|
||||||
GPIO_NUM_47, // B
|
|
||||||
GPIO_NUM_39, // B
|
|
||||||
GPIO_NUM_11, // G
|
|
||||||
GPIO_NUM_12, // G
|
|
||||||
GPIO_NUM_13, // G
|
|
||||||
GPIO_NUM_14, // G
|
|
||||||
GPIO_NUM_15, // G
|
|
||||||
GPIO_NUM_16, // G
|
|
||||||
GPIO_NUM_6, // R
|
|
||||||
GPIO_NUM_7, // R
|
|
||||||
GPIO_NUM_8, // R
|
|
||||||
GPIO_NUM_9, // R
|
|
||||||
GPIO_NUM_10, // R
|
|
||||||
},
|
|
||||||
.flags = {
|
|
||||||
.disp_active_low = false,
|
|
||||||
.refresh_on_demand = false,
|
|
||||||
.fb_in_psram = true,
|
|
||||||
.double_fb = true,
|
|
||||||
.no_fb = false,
|
|
||||||
.bb_invalidate_cache = false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
RgbDisplay::BufferConfiguration buffer_config = {
|
|
||||||
.size = (800 * 480),
|
|
||||||
.useSpi = true,
|
|
||||||
.doubleBuffer = true,
|
|
||||||
.bounceBufferMode = true,
|
|
||||||
.avoidTearing = false
|
|
||||||
};
|
|
||||||
|
|
||||||
auto configuration = std::make_unique<RgbDisplay::Configuration>(
|
|
||||||
rgb_panel_config,
|
|
||||||
buffer_config,
|
|
||||||
touch,
|
|
||||||
LV_COLOR_FORMAT_RGB565,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
driver::pwmbacklight::setBacklightDuty
|
|
||||||
);
|
|
||||||
|
|
||||||
return std::make_shared<RgbDisplay>(std::move(configuration));
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Tactility/hal/display/DisplayDevice.h>
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
#include <tactility/module.h>
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
static error_t start() {
|
|
||||||
// Empty for now
|
|
||||||
return ERROR_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static error_t stop() {
|
|
||||||
// Empty for now
|
|
||||||
return ERROR_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Module btt_panda_touch_module = {
|
|
||||||
.name = "btt-panda-touch",
|
|
||||||
.start = start,
|
|
||||||
.stop = stop,
|
|
||||||
.symbols = nullptr,
|
|
||||||
.internal = nullptr
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -2,15 +2,25 @@
|
|||||||
|
|
||||||
#include <tactility/bindings/root.h>
|
#include <tactility/bindings/root.h>
|
||||||
#include <tactility/bindings/esp32_ble.h>
|
#include <tactility/bindings/esp32_ble.h>
|
||||||
|
#include <tactility/bindings/esp32_wifi_pinned.h>
|
||||||
#include <tactility/bindings/esp32_gpio.h>
|
#include <tactility/bindings/esp32_gpio.h>
|
||||||
#include <tactility/bindings/esp32_i2c.h>
|
#include <tactility/bindings/esp32_i2c.h>
|
||||||
#include <tactility/bindings/esp32_usbhost.h>
|
#include <tactility/bindings/esp32_usbhost.h>
|
||||||
|
#include <tactility/bindings/esp32_pwm_ledc.h>
|
||||||
|
#include <tactility/bindings/pwm_backlight.h>
|
||||||
|
#include <bindings/rgb_display.h>
|
||||||
|
#include <bindings/gt911.h>
|
||||||
|
|
||||||
// Reference: https://github.com/bigtreetech/PandaTouch_PlatformIO/blob/master/docs/PINOUT.md
|
// Reference: https://github.com/bigtreetech/PandaTouch_PlatformIO/blob/master/docs/PINOUT.md
|
||||||
/ {
|
/ {
|
||||||
compatible = "root";
|
compatible = "root";
|
||||||
model = "BigTreeTech Panda Touch";
|
model = "BigTreeTech Panda Touch";
|
||||||
|
|
||||||
|
wifi0 {
|
||||||
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
ble0 {
|
ble0 {
|
||||||
compatible = "espressif,esp32-ble";
|
compatible = "espressif,esp32-ble";
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
@ -27,6 +37,15 @@
|
|||||||
clock-frequency = <400000>;
|
clock-frequency = <400000>;
|
||||||
pin-sda = <&gpio0 2 GPIO_FLAG_NONE>;
|
pin-sda = <&gpio0 2 GPIO_FLAG_NONE>;
|
||||||
pin-scl = <&gpio0 1 GPIO_FLAG_NONE>;
|
pin-scl = <&gpio0 1 GPIO_FLAG_NONE>;
|
||||||
|
|
||||||
|
touch0 {
|
||||||
|
// Reset pin 41 and interrupt pin 40 exist on the board but are not wired up here
|
||||||
|
// (unverified - left disconnected like the original deprecated-HAL config).
|
||||||
|
compatible = "goodix,gt911";
|
||||||
|
reg = <0x5D>;
|
||||||
|
x-max = <800>;
|
||||||
|
y-max = <480>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
i2c_external: i2c1 {
|
i2c_external: i2c1 {
|
||||||
@ -37,6 +56,57 @@
|
|||||||
pin-scl = <&gpio0 3 GPIO_FLAG_NONE>;
|
pin-scl = <&gpio0 3 GPIO_FLAG_NONE>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
display_backlight_pwm {
|
||||||
|
compatible = "espressif,esp32-pwm-ledc";
|
||||||
|
pin = <&gpio0 21 GPIO_FLAG_NONE>;
|
||||||
|
period-ns = <33333>;
|
||||||
|
ledc-timer = <0>;
|
||||||
|
ledc-channel = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
display_backlight {
|
||||||
|
compatible = "pwm-backlight";
|
||||||
|
pwm = <&display_backlight_pwm>;
|
||||||
|
};
|
||||||
|
|
||||||
|
display0 {
|
||||||
|
compatible = "espressif,esp32-rgb-display";
|
||||||
|
horizontal-resolution = <800>;
|
||||||
|
vertical-resolution = <480>;
|
||||||
|
pixel-clock-hz = <14000000>;
|
||||||
|
hsync-pulse-width = <4>;
|
||||||
|
hsync-back-porch = <8>;
|
||||||
|
hsync-front-porch = <8>;
|
||||||
|
vsync-pulse-width = <4>;
|
||||||
|
vsync-back-porch = <16>;
|
||||||
|
vsync-front-porch = <16>;
|
||||||
|
pclk-active-neg;
|
||||||
|
pclk-idle-high;
|
||||||
|
num-fbs = <2>;
|
||||||
|
double-fb;
|
||||||
|
bounce-buffer-size-px = <8000>;
|
||||||
|
pin-de = <&gpio0 38 GPIO_FLAG_NONE>;
|
||||||
|
pin-pclk = <&gpio0 5 GPIO_FLAG_NONE>;
|
||||||
|
pin-reset = <&gpio0 46 GPIO_FLAG_NONE>;
|
||||||
|
pin-data0 = <&gpio0 17 GPIO_FLAG_NONE>; // B
|
||||||
|
pin-data1 = <&gpio0 18 GPIO_FLAG_NONE>; // B
|
||||||
|
pin-data2 = <&gpio0 48 GPIO_FLAG_NONE>; // B
|
||||||
|
pin-data3 = <&gpio0 47 GPIO_FLAG_NONE>; // B
|
||||||
|
pin-data4 = <&gpio0 39 GPIO_FLAG_NONE>; // B
|
||||||
|
pin-data5 = <&gpio0 11 GPIO_FLAG_NONE>; // G
|
||||||
|
pin-data6 = <&gpio0 12 GPIO_FLAG_NONE>; // G
|
||||||
|
pin-data7 = <&gpio0 13 GPIO_FLAG_NONE>; // G
|
||||||
|
pin-data8 = <&gpio0 14 GPIO_FLAG_NONE>; // G
|
||||||
|
pin-data9 = <&gpio0 15 GPIO_FLAG_NONE>; // G
|
||||||
|
pin-data10 = <&gpio0 16 GPIO_FLAG_NONE>; // G
|
||||||
|
pin-data11 = <&gpio0 6 GPIO_FLAG_NONE>; // R
|
||||||
|
pin-data12 = <&gpio0 7 GPIO_FLAG_NONE>; // R
|
||||||
|
pin-data13 = <&gpio0 8 GPIO_FLAG_NONE>; // R
|
||||||
|
pin-data14 = <&gpio0 9 GPIO_FLAG_NONE>; // R
|
||||||
|
pin-data15 = <&gpio0 10 GPIO_FLAG_NONE>; // R
|
||||||
|
backlight = <&display_backlight>;
|
||||||
|
};
|
||||||
|
|
||||||
usbhost0 {
|
usbhost0 {
|
||||||
compatible = "espressif,esp32-usbhost";
|
compatible = "espressif,esp32-usbhost";
|
||||||
|
|
||||||
|
|||||||
@ -1,25 +1,22 @@
|
|||||||
[general]
|
general.vendor=BigTreeTech
|
||||||
vendor=BigTreeTech
|
general.name=Panda Touch,K Touch
|
||||||
name=Panda Touch,K Touch
|
|
||||||
|
|
||||||
[apps]
|
apps.launcherAppId=tactility.launcher
|
||||||
launcherAppId=Launcher
|
|
||||||
|
|
||||||
[hardware]
|
hardware.target=ESP32S3
|
||||||
target=ESP32S3
|
hardware.flashSize=16MB
|
||||||
flashSize=16MB
|
hardware.spiRam=true
|
||||||
spiRam=true
|
hardware.spiRamMode=OCT
|
||||||
spiRamMode=OCT
|
hardware.spiRamSpeed=120M
|
||||||
spiRamSpeed=120M
|
hardware.esptoolFlashFreq=120M
|
||||||
esptoolFlashFreq=120M
|
hardware.bluetooth=true
|
||||||
bluetooth=true
|
hardware.usbHostEnabled=true
|
||||||
usbHostEnabled=true
|
|
||||||
|
|
||||||
[display]
|
storage.userDataLocation=Internal
|
||||||
size=5"
|
|
||||||
shape=rectangle
|
|
||||||
dpi=187
|
|
||||||
|
|
||||||
[lvgl]
|
display.size=5"
|
||||||
colorDepth=16
|
display.shape=rectangle
|
||||||
fontSize=18
|
display.dpi=187
|
||||||
|
|
||||||
|
lvgl.colorDepth=16
|
||||||
|
lvgl.fontSize=18
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
- Platforms/platform-esp32
|
- Platforms/platform-esp32
|
||||||
|
- Drivers/rgb-display-module
|
||||||
|
- Drivers/gt911-module
|
||||||
dts: bigtreetech,panda-touch.dts
|
dts: bigtreetech,panda-touch.dts
|
||||||
|
|||||||
9
Devices/btt-panda-touch/source/module.cpp
Normal file
9
Devices/btt-panda-touch/source/module.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include <tactility/module.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
Module btt_panda_touch_module = {
|
||||||
|
.name = "btt-panda-touch"
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
7
Devices/cl32/CMakeLists.txt
Normal file
7
Devices/cl32/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
|
||||||
|
|
||||||
|
idf_component_register(
|
||||||
|
SRCS ${SOURCE_FILES}
|
||||||
|
INCLUDE_DIRS "source"
|
||||||
|
REQUIRES TactilityKernel
|
||||||
|
)
|
||||||
195
Devices/cl32/LICENSE-Apache-2.0.md
Normal file
195
Devices/cl32/LICENSE-Apache-2.0.md
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
Apache License
|
||||||
|
==============
|
||||||
|
|
||||||
|
_Version 2.0, January 2004_
|
||||||
|
_<<http://www.apache.org/licenses/>>_
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
121
Devices/cl32/cl32.dts
Normal file
121
Devices/cl32/cl32.dts
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
/dts-v1/;
|
||||||
|
|
||||||
|
#include <tactility/bindings/root.h>
|
||||||
|
#include <tactility/bindings/esp32_gpio.h>
|
||||||
|
#include <tactility/bindings/esp32_grove.h>
|
||||||
|
#include <tactility/bindings/esp32_i2c.h>
|
||||||
|
#include <tactility/bindings/esp32_spi.h>
|
||||||
|
#include <tactility/bindings/esp32_sdspi.h>
|
||||||
|
#include <tactility/bindings/esp32_wifi.h>
|
||||||
|
|
||||||
|
#include <bindings/tca8418.h>
|
||||||
|
#include <bindings/bm8563.h>
|
||||||
|
#include <bindings/esp_epaper.h>
|
||||||
|
|
||||||
|
/ {
|
||||||
|
compatible = "root";
|
||||||
|
model = "CL-32";
|
||||||
|
|
||||||
|
gpio0 {
|
||||||
|
compatible = "espressif,esp32-gpio";
|
||||||
|
gpio-count = <49>;
|
||||||
|
};
|
||||||
|
|
||||||
|
wifi0 {
|
||||||
|
compatible = "espressif,esp32-wifi";
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
|
// Top Stemma/Qwiic port 1 shares this bus with the keyboard and RTC (SDA: pin 1 <-> SCL: pin 2).
|
||||||
|
// External I2C devices on that port are reachable via i2c0.
|
||||||
|
i2c0 {
|
||||||
|
compatible = "espressif,esp32-i2c";
|
||||||
|
port = <I2C_NUM_0>;
|
||||||
|
clock-frequency = <100000>;
|
||||||
|
pin-sda = <&gpio0 1 GPIO_FLAG_PULL_UP>;
|
||||||
|
pin-scl = <&gpio0 2 GPIO_FLAG_PULL_UP>;
|
||||||
|
|
||||||
|
keyboard {
|
||||||
|
compatible = "ti,tca8418";
|
||||||
|
reg = <0x34>;
|
||||||
|
rows = <8>;
|
||||||
|
columns = <10>;
|
||||||
|
keymap-lc = [
|
||||||
|
37 49 50 51 52 53 54 55 56 0 // % 1 2 3 4 5 6 7 8
|
||||||
|
57 48 8 91 93 43 34 39 27 0 // 9 0 BKSP [ ] + " ' EXIT
|
||||||
|
9 113 119 101 114 116 121 117 105 0 // TAB q w e r t y u i
|
||||||
|
111 112 10 40 41 45 59 58 3 0 // o p ENTER ( ) - ; : STOP
|
||||||
|
0 97 115 100 102 103 104 106 107 0 // a s d f g h j k
|
||||||
|
108 17 35 123 125 42 44 46 2 0 // l UP # { } * , . MENU
|
||||||
|
122 120 99 118 98 32 32 110 109 0 // z x c v b n m
|
||||||
|
20 18 19 60 62 47 92 61 10 0 // LEFT DOWN RIGHT < > / \ = RUN
|
||||||
|
];
|
||||||
|
keymap-uc = [
|
||||||
|
37 49 50 51 52 53 54 55 56 0 // % 1 2 3 4 5 6 7 8
|
||||||
|
57 48 8 91 93 43 34 39 27 0 // 9 0 BKSP [ ] + " ' EXIT
|
||||||
|
9 81 87 69 82 84 89 85 73 0 // TAB Q W E R T Y U I
|
||||||
|
79 80 10 40 41 45 59 58 3 0 // O P ENTER ( ) - ; : STOP
|
||||||
|
0 65 83 68 70 71 72 74 75 0 // A S D F G H J K
|
||||||
|
76 17 35 123 125 42 44 46 2 0 // L UP # { } * , . MENU
|
||||||
|
90 88 67 86 66 32 32 78 77 0 // Z X C V B N M
|
||||||
|
20 18 19 60 62 47 92 61 10 0 // LEFT DOWN RIGHT < > / \ = RUN
|
||||||
|
];
|
||||||
|
keymap-sy = [
|
||||||
|
37 49 50 51 52 53 54 55 56 0 // % 1 2 3 4 5 6 7 8
|
||||||
|
57 48 8 91 93 43 34 39 27 0 // 9 0 BKSP [ ] + " ' EXIT
|
||||||
|
9 113 119 101 114 116 121 117 105 0 // TAB q w e r t y u i
|
||||||
|
111 112 10 40 41 45 59 58 3 0 // o p ENTER ( ) - ; : STOP
|
||||||
|
0 97 115 100 102 103 104 106 107 0 // a s d f g h j k
|
||||||
|
108 17 35 123 125 42 44 46 2 0 // l UP # { } * , . MENU
|
||||||
|
122 120 99 118 98 32 32 110 109 0 // z x c v b n m
|
||||||
|
20 18 19 60 62 47 92 61 10 0 // LEFT DOWN RIGHT < > / \ = RUN
|
||||||
|
];
|
||||||
|
shift-row = <4>;
|
||||||
|
shift-col = <0>;
|
||||||
|
sym-row = <5>;
|
||||||
|
sym-col = <8>;
|
||||||
|
};
|
||||||
|
|
||||||
|
rtc: bm8563 {
|
||||||
|
compatible = "belling,bm8563";
|
||||||
|
reg = <0x51>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
port_b: grove1 {
|
||||||
|
compatible = "espressif,esp32-grove";
|
||||||
|
defaultMode = <GROVE_MODE_I2C>;
|
||||||
|
pinSdaTx = <&gpio0 47 GPIO_FLAG_NONE>;
|
||||||
|
pinSclRx = <&gpio0 48 GPIO_FLAG_NONE>;
|
||||||
|
uartPort = <UART_NUM_1>;
|
||||||
|
i2cPort = <I2C_NUM_1>;
|
||||||
|
i2cClockFrequency = <100000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
spi1 {
|
||||||
|
compatible = "espressif,esp32-spi";
|
||||||
|
host = <SPI2_HOST>;
|
||||||
|
cs-gpios = <&gpio0 7 GPIO_FLAG_NONE>;
|
||||||
|
pin-mosi = <&gpio0 10 GPIO_FLAG_NONE>;
|
||||||
|
pin-miso = <&gpio0 11 GPIO_FLAG_NONE>;
|
||||||
|
pin-sclk = <&gpio0 9 GPIO_FLAG_NONE>;
|
||||||
|
max-transfer-size = <4096>;
|
||||||
|
|
||||||
|
sdcard@0 {
|
||||||
|
compatible = "espressif,esp32-sdspi";
|
||||||
|
frequency-khz = <20000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
epd@1 {
|
||||||
|
compatible = "tuanpmt,esp-epaper";
|
||||||
|
pin-dc = <&gpio0 13 GPIO_FLAG_NONE>;
|
||||||
|
pin-reset = <&gpio0 12 GPIO_FLAG_NONE>;
|
||||||
|
pin-busy = <&gpio0 14 GPIO_FLAG_NONE>;
|
||||||
|
pin-cs = <&gpio0 6 GPIO_FLAG_NONE>;
|
||||||
|
clock-speed-hz = <4000000>;
|
||||||
|
panel-type = "gdey029t71h";
|
||||||
|
update-mode = <2>;
|
||||||
|
rotation = <1>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
22
Devices/cl32/device.properties
Normal file
22
Devices/cl32/device.properties
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
general.vendor=CL-32
|
||||||
|
general.name=CL-32
|
||||||
|
|
||||||
|
apps.launcherAppId=tactility.launcher
|
||||||
|
|
||||||
|
hardware.target=ESP32S3
|
||||||
|
hardware.flashSize=8MB
|
||||||
|
hardware.spiRam=true
|
||||||
|
hardware.spiRamMode=QUAD
|
||||||
|
hardware.spiRamSpeed=80M
|
||||||
|
hardware.spiRamXipDisabled=true
|
||||||
|
hardware.esptoolFlashFreq=80M
|
||||||
|
hardware.bluetooth=true
|
||||||
|
|
||||||
|
storage.userDataLocation=SD
|
||||||
|
|
||||||
|
display.size=2.9"
|
||||||
|
display.shape=rectangle
|
||||||
|
display.dpi=139
|
||||||
|
|
||||||
|
lvgl.colorDepth=8
|
||||||
|
lvgl.theme=Mono
|
||||||
6
Devices/cl32/devicetree.yaml
Normal file
6
Devices/cl32/devicetree.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
dependencies:
|
||||||
|
- Platforms/platform-esp32
|
||||||
|
- Drivers/tca8418-module
|
||||||
|
- Drivers/esp-epaper-module
|
||||||
|
- Drivers/bm8563-module
|
||||||
|
dts: cl32.dts
|
||||||
9
Devices/cl32/source/module.cpp
Normal file
9
Devices/cl32/source/module.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include <tactility/module.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
struct Module cl32_module = {
|
||||||
|
.name = "cl32"
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS ${SOURCE_FILES}
|
SRCS ${SOURCE_FILES}
|
||||||
INCLUDE_DIRS "Source"
|
REQUIRES TactilityKernel
|
||||||
REQUIRES Tactility esp_lvgl_port ILI934x CST816S PwmBacklight driver vfs fatfs
|
|
||||||
)
|
)
|
||||||
|
|||||||
195
Devices/cyd-2432s024c/LICENSE-Apache-2.0.md
Normal file
195
Devices/cyd-2432s024c/LICENSE-Apache-2.0.md
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
Apache License
|
||||||
|
==============
|
||||||
|
|
||||||
|
_Version 2.0, January 2004_
|
||||||
|
_<<http://www.apache.org/licenses/>>_
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
#include "devices/Display.h"
|
|
||||||
#include <driver/gpio.h>
|
|
||||||
|
|
||||||
#include <Tactility/hal/Configuration.h>
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
|
|
||||||
static bool initBoot() {
|
|
||||||
// Set the RGB LED Pins to output and turn them off
|
|
||||||
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT)); // Red
|
|
||||||
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_16, GPIO_MODE_OUTPUT)); // Green
|
|
||||||
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_17, GPIO_MODE_OUTPUT)); // Blue
|
|
||||||
|
|
||||||
// 0 on, 1 off
|
|
||||||
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_4, 1)); // Red
|
|
||||||
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_16, 1)); // Green
|
|
||||||
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_17, 1)); // Blue
|
|
||||||
|
|
||||||
return driver::pwmbacklight::init(LCD_PIN_BACKLIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static tt::hal::DeviceVector createDevices() {
|
|
||||||
return {
|
|
||||||
createDisplay(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extern const tt::hal::Configuration hardwareConfiguration = {
|
|
||||||
.initBoot = initBoot,
|
|
||||||
.createDevices = createDevices
|
|
||||||
};
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
#include "Display.h"
|
|
||||||
#include "Cst816Touch.h"
|
|
||||||
|
|
||||||
#include <Ili934xDisplay.h>
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
|
|
||||||
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
|
|
||||||
// Note: GPIO 25 for reset and GPIO 21 for interrupt?
|
|
||||||
auto configuration = std::make_unique<Cst816sTouch::Configuration>(
|
|
||||||
I2C_NUM_0,
|
|
||||||
240,
|
|
||||||
320
|
|
||||||
);
|
|
||||||
|
|
||||||
return std::make_shared<Cst816sTouch>(std::move(configuration));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
|
||||||
Ili934xDisplay::Configuration panel_configuration = {
|
|
||||||
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
|
|
||||||
.verticalResolution = LCD_VERTICAL_RESOLUTION,
|
|
||||||
.gapX = 0,
|
|
||||||
.gapY = 0,
|
|
||||||
.swapXY = false,
|
|
||||||
.mirrorX = true,
|
|
||||||
.mirrorY = false,
|
|
||||||
.invertColor = false,
|
|
||||||
.swapBytes = true,
|
|
||||||
.bufferSize = LCD_BUFFER_SIZE,
|
|
||||||
.touch = createTouch(),
|
|
||||||
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
|
|
||||||
.resetPin = GPIO_NUM_NC,
|
|
||||||
.rgbElementOrder = LCD_RGB_ELEMENT_ORDER_BGR
|
|
||||||
};
|
|
||||||
|
|
||||||
auto spi_configuration = std::make_shared<Ili934xDisplay::SpiConfiguration>(Ili934xDisplay::SpiConfiguration {
|
|
||||||
.spiHostDevice = LCD_SPI_HOST,
|
|
||||||
.csPin = LCD_PIN_CS,
|
|
||||||
.dcPin = LCD_PIN_DC,
|
|
||||||
.pixelClockFrequency = 40'000'000,
|
|
||||||
.transactionQueueDepth = 10
|
|
||||||
});
|
|
||||||
|
|
||||||
return std::make_shared<Ili934xDisplay>(panel_configuration, spi_configuration, true);
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Tactility/hal/display/DisplayDevice.h>
|
|
||||||
#include <driver/gpio.h>
|
|
||||||
#include <driver/spi_common.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
// Display
|
|
||||||
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_27;
|
|
||||||
constexpr auto LCD_SPI_HOST = SPI2_HOST;
|
|
||||||
constexpr auto LCD_PIN_CS = GPIO_NUM_15;
|
|
||||||
constexpr auto LCD_PIN_DC = GPIO_NUM_2;
|
|
||||||
constexpr auto LCD_HORIZONTAL_RESOLUTION = 240;
|
|
||||||
constexpr auto LCD_VERTICAL_RESOLUTION = 320;
|
|
||||||
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 10;
|
|
||||||
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
#include <tactility/module.h>
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
static error_t start() {
|
|
||||||
// Empty for now
|
|
||||||
return ERROR_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static error_t stop() {
|
|
||||||
// Empty for now
|
|
||||||
return ERROR_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Module cyd_2432s024c_module = {
|
|
||||||
.name = "cyd-2432s024c",
|
|
||||||
.start = start,
|
|
||||||
.stop = stop,
|
|
||||||
.symbols = nullptr,
|
|
||||||
.internal = nullptr
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,16 +1,26 @@
|
|||||||
/dts-v1/;
|
/dts-v1/;
|
||||||
|
|
||||||
#include <tactility/bindings/root.h>
|
#include <tactility/bindings/root.h>
|
||||||
|
#include <tactility/bindings/esp32_wifi_pinned.h>
|
||||||
#include <tactility/bindings/esp32_gpio.h>
|
#include <tactility/bindings/esp32_gpio.h>
|
||||||
#include <tactility/bindings/esp32_i2c.h>
|
#include <tactility/bindings/esp32_i2c.h>
|
||||||
#include <tactility/bindings/esp32_spi.h>
|
#include <tactility/bindings/esp32_spi.h>
|
||||||
#include <tactility/bindings/esp32_sdspi.h>
|
#include <tactility/bindings/esp32_sdspi.h>
|
||||||
#include <tactility/bindings/display_placeholder.h>
|
#include <tactility/bindings/esp32_pwm_ledc.h>
|
||||||
|
#include <tactility/bindings/pwm_backlight.h>
|
||||||
|
#include <tactility/bindings/rgb_led_pwm.h>
|
||||||
|
#include <bindings/ili9341.h>
|
||||||
|
#include <bindings/cst816s.h>
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
compatible = "root";
|
compatible = "root";
|
||||||
model = "CYD 2432S024C";
|
model = "CYD 2432S024C";
|
||||||
|
|
||||||
|
wifi0 {
|
||||||
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
compatible = "espressif,esp32-gpio";
|
compatible = "espressif,esp32-gpio";
|
||||||
gpio-count = <40>;
|
gpio-count = <40>;
|
||||||
@ -22,6 +32,66 @@
|
|||||||
clock-frequency = <400000>;
|
clock-frequency = <400000>;
|
||||||
pin-sda = <&gpio0 33 GPIO_FLAG_NONE>;
|
pin-sda = <&gpio0 33 GPIO_FLAG_NONE>;
|
||||||
pin-scl = <&gpio0 32 GPIO_FLAG_NONE>;
|
pin-scl = <&gpio0 32 GPIO_FLAG_NONE>;
|
||||||
|
|
||||||
|
touch {
|
||||||
|
compatible = "hynitron,cst816s";
|
||||||
|
reg = <0x15>;
|
||||||
|
x-max = <240>;
|
||||||
|
y-max = <320>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
rgb_led_channel_red {
|
||||||
|
compatible = "espressif,esp32-pwm-ledc";
|
||||||
|
pin = <&gpio0 4 GPIO_FLAG_NONE>;
|
||||||
|
period-ns = <500000>;
|
||||||
|
ledc-timer = <0>;
|
||||||
|
ledc-channel = <0>;
|
||||||
|
inverted;
|
||||||
|
};
|
||||||
|
|
||||||
|
rgb_led_channel_green {
|
||||||
|
compatible = "espressif,esp32-pwm-ledc";
|
||||||
|
pin = <&gpio0 16 GPIO_FLAG_NONE>;
|
||||||
|
period-ns = <500000>;
|
||||||
|
ledc-timer = <0>;
|
||||||
|
ledc-channel = <1>;
|
||||||
|
inverted;
|
||||||
|
};
|
||||||
|
|
||||||
|
rgb_led_channel_blue {
|
||||||
|
compatible = "espressif,esp32-pwm-ledc";
|
||||||
|
pin = <&gpio0 17 GPIO_FLAG_NONE>;
|
||||||
|
period-ns = <500000>;
|
||||||
|
ledc-timer = <0>;
|
||||||
|
ledc-channel = <2>;
|
||||||
|
inverted;
|
||||||
|
};
|
||||||
|
|
||||||
|
rgb_led_pwm {
|
||||||
|
compatible = "rgb-led-pwm";
|
||||||
|
pwm-red = <&rgb_led_channel_red>;
|
||||||
|
pwm-green = <&rgb_led_channel_green>;
|
||||||
|
pwm-blue = <&rgb_led_channel_blue>;
|
||||||
|
// Default is red, and we want to reset it to off by default
|
||||||
|
default-color = <0 0 0>;
|
||||||
|
enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
display_backlight_pwm {
|
||||||
|
compatible = "espressif,esp32-pwm-ledc";
|
||||||
|
pin = <&gpio0 27 GPIO_FLAG_NONE>;
|
||||||
|
period-ns = <1953125>;
|
||||||
|
ledc-timer = <1>;
|
||||||
|
ledc-channel = <3>;
|
||||||
|
};
|
||||||
|
|
||||||
|
display_backlight {
|
||||||
|
compatible = "pwm-backlight";
|
||||||
|
// Off by default so display power-on won't show the screen from before the last power loss.
|
||||||
|
// The display backlight is turned on during the boot process.
|
||||||
|
status = "disabled";
|
||||||
|
pwm = <&display_backlight_pwm>;
|
||||||
};
|
};
|
||||||
|
|
||||||
spi0 {
|
spi0 {
|
||||||
@ -31,8 +101,15 @@
|
|||||||
pin-mosi = <&gpio0 13 GPIO_FLAG_NONE>;
|
pin-mosi = <&gpio0 13 GPIO_FLAG_NONE>;
|
||||||
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
|
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
|
||||||
|
|
||||||
display {
|
display@0 {
|
||||||
compatible = "display-placeholder";
|
compatible = "ilitek,ili9341";
|
||||||
|
horizontal-resolution = <240>;
|
||||||
|
vertical-resolution = <320>;
|
||||||
|
mirror-x;
|
||||||
|
bgr-order;
|
||||||
|
pixel-clock-hz = <40000000>;
|
||||||
|
pin-dc = <&gpio0 2 GPIO_FLAG_NONE>;
|
||||||
|
backlight = <&display_backlight>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,6 +120,7 @@
|
|||||||
pin-mosi = <&gpio0 23 GPIO_FLAG_NONE>;
|
pin-mosi = <&gpio0 23 GPIO_FLAG_NONE>;
|
||||||
pin-miso = <&gpio0 19 GPIO_FLAG_NONE>;
|
pin-miso = <&gpio0 19 GPIO_FLAG_NONE>;
|
||||||
pin-sclk = <&gpio0 18 GPIO_FLAG_NONE>;
|
pin-sclk = <&gpio0 18 GPIO_FLAG_NONE>;
|
||||||
|
miso-pull-up;
|
||||||
|
|
||||||
sdcard@0 {
|
sdcard@0 {
|
||||||
compatible = "espressif,esp32-sdspi";
|
compatible = "espressif,esp32-sdspi";
|
||||||
|
|||||||
@ -1,19 +1,16 @@
|
|||||||
[general]
|
general.vendor=CYD
|
||||||
vendor=CYD
|
general.name=2432S024C
|
||||||
name=2432S024C
|
|
||||||
|
|
||||||
[apps]
|
apps.launcherAppId=tactility.launcher
|
||||||
launcherAppId=Launcher
|
|
||||||
|
|
||||||
[hardware]
|
hardware.target=ESP32
|
||||||
target=ESP32
|
hardware.flashSize=4MB
|
||||||
flashSize=4MB
|
hardware.spiRam=false
|
||||||
spiRam=false
|
|
||||||
|
|
||||||
[display]
|
storage.userDataLocation=SD
|
||||||
size=2.4"
|
|
||||||
shape=rectangle
|
|
||||||
dpi=167
|
|
||||||
|
|
||||||
[lvgl]
|
display.size=2.4"
|
||||||
colorDepth=16
|
display.shape=rectangle
|
||||||
|
display.dpi=167
|
||||||
|
|
||||||
|
lvgl.colorDepth=16
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
- Platforms/platform-esp32
|
- Platforms/platform-esp32
|
||||||
|
- Drivers/ili9341-module
|
||||||
|
- Drivers/cst816s-module
|
||||||
dts: cyd,2432s024c.dts
|
dts: cyd,2432s024c.dts
|
||||||
|
|||||||
9
Devices/cyd-2432s024c/source/module.cpp
Normal file
9
Devices/cyd-2432s024c/source/module.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include <tactility/module.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
Module cyd_2432s024c_module = {
|
||||||
|
.name = "cyd-2432s024c",
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS ${SOURCE_FILES}
|
SRCS ${SOURCE_FILES}
|
||||||
INCLUDE_DIRS "Source"
|
REQUIRES TactilityKernel
|
||||||
REQUIRES Tactility esp_lvgl_port ILI934x XPT2046 PwmBacklight driver vfs fatfs
|
|
||||||
)
|
)
|
||||||
|
|||||||
195
Devices/cyd-2432s024r/LICENSE-Apache-2.0.md
Normal file
195
Devices/cyd-2432s024r/LICENSE-Apache-2.0.md
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
Apache License
|
||||||
|
==============
|
||||||
|
|
||||||
|
_Version 2.0, January 2004_
|
||||||
|
_<<http://www.apache.org/licenses/>>_
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
#include "devices/Display.h"
|
|
||||||
|
|
||||||
#include <Tactility/hal/Configuration.h>
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
|
|
||||||
using namespace tt::hal;
|
|
||||||
|
|
||||||
static bool initBoot() {
|
|
||||||
return driver::pwmbacklight::init(LCD_PIN_BACKLIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static DeviceVector createDevices() {
|
|
||||||
return {
|
|
||||||
createDisplay()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extern const Configuration hardwareConfiguration = {
|
|
||||||
.initBoot = initBoot,
|
|
||||||
.createDevices = createDevices
|
|
||||||
};
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
#include "Display.h"
|
|
||||||
#include "Xpt2046Touch.h"
|
|
||||||
#include <Ili934xDisplay.h>
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
|
|
||||||
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch(esp_lcd_spi_bus_handle_t spiDevice) {
|
|
||||||
auto configuration = std::make_unique<Xpt2046Touch::Configuration>(
|
|
||||||
spiDevice,
|
|
||||||
TOUCH_CS_PIN,
|
|
||||||
LCD_HORIZONTAL_RESOLUTION,
|
|
||||||
LCD_VERTICAL_RESOLUTION,
|
|
||||||
true, // swapXY
|
|
||||||
false, // mirrorX
|
|
||||||
true // mirrorY
|
|
||||||
);
|
|
||||||
return std::make_shared<Xpt2046Touch>(std::move(configuration));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
|
||||||
auto spi_configuration = std::make_shared<Ili934xDisplay::SpiConfiguration>(Ili934xDisplay::SpiConfiguration {
|
|
||||||
.spiHostDevice = LCD_SPI_HOST,
|
|
||||||
.csPin = LCD_PIN_CS,
|
|
||||||
.dcPin = LCD_PIN_DC,
|
|
||||||
.pixelClockFrequency = 40'000'000,
|
|
||||||
.transactionQueueDepth = 10
|
|
||||||
});
|
|
||||||
|
|
||||||
Ili934xDisplay::Configuration panel_configuration = {
|
|
||||||
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
|
|
||||||
.verticalResolution = LCD_VERTICAL_RESOLUTION,
|
|
||||||
.gapX = 0,
|
|
||||||
.gapY = 0,
|
|
||||||
.swapXY = true,
|
|
||||||
.mirrorX = true,
|
|
||||||
.mirrorY = true,
|
|
||||||
.invertColor = false,
|
|
||||||
.swapBytes = true,
|
|
||||||
.bufferSize = LCD_BUFFER_SIZE,
|
|
||||||
.touch = createTouch(spi_configuration->spiHostDevice),
|
|
||||||
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
|
|
||||||
.resetPin = LCD_PIN_RST,
|
|
||||||
.rgbElementOrder = LCD_RGB_ELEMENT_ORDER_RGB
|
|
||||||
};
|
|
||||||
|
|
||||||
return std::make_shared<Ili934xDisplay>(panel_configuration, spi_configuration, true);
|
|
||||||
}
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Tactility/hal/display/DisplayDevice.h>
|
|
||||||
#include <driver/gpio.h>
|
|
||||||
#include <driver/spi_common.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
// Display
|
|
||||||
constexpr auto LCD_SPI_HOST = SPI2_HOST;
|
|
||||||
constexpr auto LCD_PIN_CS = GPIO_NUM_15;
|
|
||||||
constexpr auto LCD_PIN_DC = GPIO_NUM_2;
|
|
||||||
constexpr auto LCD_PIN_RST = GPIO_NUM_NC; // tied to ESP32 RST
|
|
||||||
constexpr auto LCD_PIN_CLK = GPIO_NUM_14;
|
|
||||||
constexpr auto LCD_PIN_MOSI = GPIO_NUM_13;
|
|
||||||
constexpr auto LCD_PIN_MISO = GPIO_NUM_12;
|
|
||||||
constexpr auto LCD_HORIZONTAL_RESOLUTION = 240;
|
|
||||||
constexpr auto LCD_VERTICAL_RESOLUTION = 320;
|
|
||||||
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 10;
|
|
||||||
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
|
|
||||||
|
|
||||||
// Backlight
|
|
||||||
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_27;
|
|
||||||
|
|
||||||
// Touch
|
|
||||||
constexpr auto TOUCH_CS_PIN = GPIO_NUM_33;
|
|
||||||
constexpr auto TOUCH_IRQ_PIN = GPIO_NUM_36;
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
#include <tactility/module.h>
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
static error_t start() {
|
|
||||||
// Empty for now
|
|
||||||
return ERROR_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static error_t stop() {
|
|
||||||
// Empty for now
|
|
||||||
return ERROR_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Module cyd_2432s024r_module = {
|
|
||||||
.name = "cyd-2432s024r",
|
|
||||||
.start = start,
|
|
||||||
.stop = stop,
|
|
||||||
.symbols = nullptr,
|
|
||||||
.internal = nullptr
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,22 +1,84 @@
|
|||||||
/dts-v1/;
|
/dts-v1/;
|
||||||
|
|
||||||
#include <tactility/bindings/root.h>
|
#include <tactility/bindings/root.h>
|
||||||
|
#include <tactility/bindings/esp32_wifi_pinned.h>
|
||||||
#include <tactility/bindings/esp32_gpio.h>
|
#include <tactility/bindings/esp32_gpio.h>
|
||||||
#include <tactility/bindings/esp32_spi.h>
|
#include <tactility/bindings/esp32_spi.h>
|
||||||
#include <tactility/bindings/esp32_sdspi.h>
|
#include <tactility/bindings/esp32_sdspi.h>
|
||||||
#include <tactility/bindings/display_placeholder.h>
|
|
||||||
#include <tactility/bindings/touch_placeholder.h>
|
|
||||||
#include <tactility/bindings/esp32_uart.h>
|
#include <tactility/bindings/esp32_uart.h>
|
||||||
|
#include <tactility/bindings/esp32_pwm_ledc.h>
|
||||||
|
#include <tactility/bindings/pwm_backlight.h>
|
||||||
|
#include <tactility/bindings/rgb_led_pwm.h>
|
||||||
|
#include <bindings/ili9341.h>
|
||||||
|
#include <bindings/xpt2046.h>
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
compatible = "root";
|
compatible = "root";
|
||||||
model = "CYD 2432S024R";
|
model = "CYD 2432S024R";
|
||||||
|
|
||||||
|
wifi0 {
|
||||||
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
compatible = "espressif,esp32-gpio";
|
compatible = "espressif,esp32-gpio";
|
||||||
gpio-count = <40>;
|
gpio-count = <40>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
rgb_led_channel_red {
|
||||||
|
compatible = "espressif,esp32-pwm-ledc";
|
||||||
|
pin = <&gpio0 4 GPIO_FLAG_NONE>;
|
||||||
|
period-ns = <500000>;
|
||||||
|
ledc-timer = <0>;
|
||||||
|
ledc-channel = <0>;
|
||||||
|
inverted;
|
||||||
|
};
|
||||||
|
|
||||||
|
rgb_led_channel_green {
|
||||||
|
compatible = "espressif,esp32-pwm-ledc";
|
||||||
|
pin = <&gpio0 16 GPIO_FLAG_NONE>;
|
||||||
|
period-ns = <500000>;
|
||||||
|
ledc-timer = <0>;
|
||||||
|
ledc-channel = <1>;
|
||||||
|
inverted;
|
||||||
|
};
|
||||||
|
|
||||||
|
rgb_led_channel_blue {
|
||||||
|
compatible = "espressif,esp32-pwm-ledc";
|
||||||
|
pin = <&gpio0 17 GPIO_FLAG_NONE>;
|
||||||
|
period-ns = <500000>;
|
||||||
|
ledc-timer = <0>;
|
||||||
|
ledc-channel = <2>;
|
||||||
|
inverted;
|
||||||
|
};
|
||||||
|
|
||||||
|
rgb_led_pwm {
|
||||||
|
compatible = "rgb-led-pwm";
|
||||||
|
pwm-red = <&rgb_led_channel_red>;
|
||||||
|
pwm-green = <&rgb_led_channel_green>;
|
||||||
|
pwm-blue = <&rgb_led_channel_blue>;
|
||||||
|
// Default is red, and we want to reset it to off by default
|
||||||
|
default-color = <0 0 0>;
|
||||||
|
enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
display_backlight_pwm {
|
||||||
|
compatible = "espressif,esp32-pwm-ledc";
|
||||||
|
pin = <&gpio0 27 GPIO_FLAG_NONE>;
|
||||||
|
period-ns = <1953125>;
|
||||||
|
ledc-timer = <1>;
|
||||||
|
ledc-channel = <3>;
|
||||||
|
};
|
||||||
|
|
||||||
|
display_backlight {
|
||||||
|
compatible = "pwm-backlight";
|
||||||
|
// Off by default so display power-on won't show the screen from before the last power loss.
|
||||||
|
// The display backlight is turned on during the boot process.
|
||||||
|
status = "disabled";
|
||||||
|
pwm = <&display_backlight_pwm>;
|
||||||
|
};
|
||||||
|
|
||||||
spi0 {
|
spi0 {
|
||||||
compatible = "espressif,esp32-spi";
|
compatible = "espressif,esp32-spi";
|
||||||
host = <SPI2_HOST>;
|
host = <SPI2_HOST>;
|
||||||
@ -27,11 +89,23 @@
|
|||||||
<&gpio0 33 GPIO_FLAG_NONE>; // Touch
|
<&gpio0 33 GPIO_FLAG_NONE>; // Touch
|
||||||
|
|
||||||
display@0 {
|
display@0 {
|
||||||
compatible = "display-placeholder";
|
compatible = "ilitek,ili9341";
|
||||||
|
horizontal-resolution = <240>;
|
||||||
|
vertical-resolution = <320>;
|
||||||
|
swap-xy;
|
||||||
|
mirror-x;
|
||||||
|
mirror-y;
|
||||||
|
pixel-clock-hz = <40000000>;
|
||||||
|
pin-dc = <&gpio0 2 GPIO_FLAG_NONE>;
|
||||||
|
backlight = <&display_backlight>;
|
||||||
};
|
};
|
||||||
|
|
||||||
touch@1 {
|
touch@1 {
|
||||||
compatible = "touch-placeholder";
|
compatible = "xptek,xpt2046";
|
||||||
|
x-max = <240>;
|
||||||
|
y-max = <320>;
|
||||||
|
swap-xy;
|
||||||
|
mirror-y;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,19 +1,16 @@
|
|||||||
[general]
|
general.vendor=CYD
|
||||||
vendor=CYD
|
general.name=2432S024R
|
||||||
name=2432S024R
|
|
||||||
|
|
||||||
[apps]
|
apps.launcherAppId=tactility.launcher
|
||||||
launcherAppId=Launcher
|
|
||||||
|
|
||||||
[hardware]
|
hardware.target=ESP32
|
||||||
target=ESP32
|
hardware.flashSize=4MB
|
||||||
flashSize=4MB
|
hardware.spiRam=false
|
||||||
spiRam=false
|
|
||||||
|
|
||||||
[display]
|
storage.userDataLocation=SD
|
||||||
size=2.4"
|
|
||||||
shape=rectangle
|
|
||||||
dpi=167
|
|
||||||
|
|
||||||
[lvgl]
|
display.size=2.4"
|
||||||
colorDepth=16
|
display.shape=rectangle
|
||||||
|
display.dpi=167
|
||||||
|
|
||||||
|
lvgl.colorDepth=16
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
- Platforms/platform-esp32
|
- Platforms/platform-esp32
|
||||||
|
- Drivers/ili9341-module
|
||||||
|
- Drivers/xpt2046-module
|
||||||
dts: cyd,2432s024r.dts
|
dts: cyd,2432s024r.dts
|
||||||
|
|||||||
9
Devices/cyd-2432s024r/source/module.cpp
Normal file
9
Devices/cyd-2432s024r/source/module.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include <tactility/module.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
Module cyd_2432s024r_module = {
|
||||||
|
.name = "cyd-2432s024r"
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS ${SOURCE_FILES}
|
SRCS ${SOURCE_FILES}
|
||||||
INCLUDE_DIRS "Source"
|
REQUIRES TactilityKernel
|
||||||
REQUIRES Tactility esp_lvgl_port ILI934x XPT2046SoftSPI PwmBacklight driver vfs fatfs
|
|
||||||
)
|
)
|
||||||
|
|||||||
195
Devices/cyd-2432s028r/LICENSE-Apache-2.0.md
Normal file
195
Devices/cyd-2432s028r/LICENSE-Apache-2.0.md
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
Apache License
|
||||||
|
==============
|
||||||
|
|
||||||
|
_Version 2.0, January 2004_
|
||||||
|
_<<http://www.apache.org/licenses/>>_
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
#include "devices/Display.h"
|
|
||||||
#include <driver/gpio.h>
|
|
||||||
|
|
||||||
#include <Tactility/hal/Configuration.h>
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
|
|
||||||
using namespace tt::hal;
|
|
||||||
|
|
||||||
static bool initBoot() {
|
|
||||||
// Set the RGB LED Pins to output and turn them off
|
|
||||||
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT)); // Red
|
|
||||||
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_16, GPIO_MODE_OUTPUT)); // Green
|
|
||||||
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_17, GPIO_MODE_OUTPUT)); // Blue
|
|
||||||
|
|
||||||
// 0 on, 1 off
|
|
||||||
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_4, 1)); // Red
|
|
||||||
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_16, 1)); // Green
|
|
||||||
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_17, 1)); // Blue
|
|
||||||
|
|
||||||
return driver::pwmbacklight::init(LCD_PIN_BACKLIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static DeviceVector createDevices() {
|
|
||||||
return {
|
|
||||||
createDisplay(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extern const Configuration hardwareConfiguration = {
|
|
||||||
.initBoot = initBoot,
|
|
||||||
.createDevices = createDevices
|
|
||||||
};
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
#include "Display.h"
|
|
||||||
#include "Xpt2046SoftSpi.h"
|
|
||||||
#include <Ili934xDisplay.h>
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
|
|
||||||
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
|
|
||||||
auto configuration = std::make_unique<Xpt2046SoftSpi::Configuration>(
|
|
||||||
TOUCH_MOSI_PIN,
|
|
||||||
TOUCH_MISO_PIN,
|
|
||||||
TOUCH_SCK_PIN,
|
|
||||||
TOUCH_CS_PIN,
|
|
||||||
LCD_HORIZONTAL_RESOLUTION,
|
|
||||||
LCD_VERTICAL_RESOLUTION,
|
|
||||||
false, // swapXY
|
|
||||||
true, // mirrorX
|
|
||||||
false // mirrorY
|
|
||||||
);
|
|
||||||
|
|
||||||
return std::make_shared<Xpt2046SoftSpi>(std::move(configuration));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
|
||||||
Ili934xDisplay::Configuration panel_configuration = {
|
|
||||||
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
|
|
||||||
.verticalResolution = LCD_VERTICAL_RESOLUTION,
|
|
||||||
.gapX = 0,
|
|
||||||
.gapY = 0,
|
|
||||||
.swapXY = false,
|
|
||||||
.mirrorX = true,
|
|
||||||
.mirrorY = false,
|
|
||||||
.invertColor = false,
|
|
||||||
.swapBytes = true,
|
|
||||||
.bufferSize = LCD_BUFFER_SIZE,
|
|
||||||
.touch = createTouch(),
|
|
||||||
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
|
|
||||||
.resetPin = GPIO_NUM_NC,
|
|
||||||
.rgbElementOrder = LCD_RGB_ELEMENT_ORDER_BGR
|
|
||||||
};
|
|
||||||
|
|
||||||
auto spi_configuration = std::make_shared<Ili934xDisplay::SpiConfiguration>(Ili934xDisplay::SpiConfiguration {
|
|
||||||
.spiHostDevice = LCD_SPI_HOST,
|
|
||||||
.csPin = LCD_PIN_CS,
|
|
||||||
.dcPin = LCD_PIN_DC,
|
|
||||||
.pixelClockFrequency = 40'000'000,
|
|
||||||
.transactionQueueDepth = 10
|
|
||||||
});
|
|
||||||
|
|
||||||
return std::make_shared<Ili934xDisplay>(panel_configuration, spi_configuration, true);
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Tactility/hal/display/DisplayDevice.h>
|
|
||||||
#include <driver/gpio.h>
|
|
||||||
#include <driver/spi_common.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
// Display
|
|
||||||
constexpr auto LCD_SPI_HOST = SPI2_HOST;
|
|
||||||
constexpr auto LCD_PIN_CS = GPIO_NUM_15;
|
|
||||||
constexpr auto LCD_PIN_DC = GPIO_NUM_2;
|
|
||||||
constexpr auto LCD_HORIZONTAL_RESOLUTION = 240;
|
|
||||||
constexpr auto LCD_VERTICAL_RESOLUTION = 320;
|
|
||||||
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 10;
|
|
||||||
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
|
|
||||||
|
|
||||||
// Display backlight (PWM)
|
|
||||||
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_21;
|
|
||||||
|
|
||||||
// Touch (Software SPI)
|
|
||||||
constexpr auto TOUCH_MISO_PIN = GPIO_NUM_39;
|
|
||||||
constexpr auto TOUCH_MOSI_PIN = GPIO_NUM_32;
|
|
||||||
constexpr auto TOUCH_SCK_PIN = GPIO_NUM_25;
|
|
||||||
constexpr auto TOUCH_CS_PIN = GPIO_NUM_33;
|
|
||||||
constexpr auto TOUCH_IRQ_PIN = GPIO_NUM_36;
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
#include <tactility/module.h>
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
static error_t start() {
|
|
||||||
// Empty for now
|
|
||||||
return ERROR_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static error_t stop() {
|
|
||||||
// Empty for now
|
|
||||||
return ERROR_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Module cyd_2432s028r_module = {
|
|
||||||
.name = "cyd-2432s028r",
|
|
||||||
.start = start,
|
|
||||||
.stop = stop,
|
|
||||||
.symbols = nullptr,
|
|
||||||
.internal = nullptr
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,18 +1,27 @@
|
|||||||
/dts-v1/;
|
/dts-v1/;
|
||||||
|
|
||||||
#include <tactility/bindings/root.h>
|
#include <tactility/bindings/root.h>
|
||||||
|
#include <tactility/bindings/esp32_wifi_pinned.h>
|
||||||
#include <tactility/bindings/esp32_gpio.h>
|
#include <tactility/bindings/esp32_gpio.h>
|
||||||
#include <tactility/bindings/esp32_i2c.h>
|
#include <tactility/bindings/esp32_i2c.h>
|
||||||
#include <tactility/bindings/esp32_spi.h>
|
#include <tactility/bindings/esp32_spi.h>
|
||||||
#include <tactility/bindings/esp32_uart.h>
|
#include <tactility/bindings/esp32_uart.h>
|
||||||
#include <tactility/bindings/esp32_sdspi.h>
|
#include <tactility/bindings/esp32_sdspi.h>
|
||||||
#include <tactility/bindings/display_placeholder.h>
|
#include <tactility/bindings/esp32_pwm_ledc.h>
|
||||||
#include <tactility/bindings/touch_placeholder.h>
|
#include <tactility/bindings/pwm_backlight.h>
|
||||||
|
#include <tactility/bindings/rgb_led_pwm.h>
|
||||||
|
#include <bindings/ili9341.h>
|
||||||
|
#include <bindings/xpt2046_softspi.h>
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
compatible = "root";
|
compatible = "root";
|
||||||
model = "CYD 2432S028R";
|
model = "CYD 2432S028R";
|
||||||
|
|
||||||
|
wifi0 {
|
||||||
|
compatible = "espressif,esp32-wifi-pinned";
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
gpio0 {
|
gpio0 {
|
||||||
compatible = "espressif,esp32-gpio";
|
compatible = "espressif,esp32-gpio";
|
||||||
gpio-count = <40>;
|
gpio-count = <40>;
|
||||||
@ -26,21 +35,87 @@
|
|||||||
pin-scl = <&gpio0 22 GPIO_FLAG_NONE>;
|
pin-scl = <&gpio0 22 GPIO_FLAG_NONE>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
rgb_led_channel_red {
|
||||||
|
compatible = "espressif,esp32-pwm-ledc";
|
||||||
|
pin = <&gpio0 4 GPIO_FLAG_NONE>;
|
||||||
|
period-ns = <500000>;
|
||||||
|
ledc-timer = <0>;
|
||||||
|
ledc-channel = <0>;
|
||||||
|
inverted;
|
||||||
|
};
|
||||||
|
|
||||||
|
rgb_led_channel_green {
|
||||||
|
compatible = "espressif,esp32-pwm-ledc";
|
||||||
|
pin = <&gpio0 16 GPIO_FLAG_NONE>;
|
||||||
|
period-ns = <500000>;
|
||||||
|
ledc-timer = <0>;
|
||||||
|
ledc-channel = <1>;
|
||||||
|
inverted;
|
||||||
|
};
|
||||||
|
|
||||||
|
rgb_led_channel_blue {
|
||||||
|
compatible = "espressif,esp32-pwm-ledc";
|
||||||
|
pin = <&gpio0 17 GPIO_FLAG_NONE>;
|
||||||
|
period-ns = <500000>;
|
||||||
|
ledc-timer = <0>;
|
||||||
|
ledc-channel = <2>;
|
||||||
|
inverted;
|
||||||
|
};
|
||||||
|
|
||||||
|
display_backlight_pwm {
|
||||||
|
compatible = "espressif,esp32-pwm-ledc";
|
||||||
|
pin = <&gpio0 21 GPIO_FLAG_NONE>;
|
||||||
|
period-ns = <1953125>;
|
||||||
|
ledc-timer = <1>;
|
||||||
|
ledc-channel = <3>;
|
||||||
|
};
|
||||||
|
|
||||||
|
rgb_led_pwm {
|
||||||
|
compatible = "rgb-led-pwm";
|
||||||
|
pwm-red = <&rgb_led_channel_red>;
|
||||||
|
pwm-green = <&rgb_led_channel_green>;
|
||||||
|
pwm-blue = <&rgb_led_channel_blue>;
|
||||||
|
// Default is red, and we want to reset it to off by default
|
||||||
|
default-color = <0 0 0>;
|
||||||
|
enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
display_backlight {
|
||||||
|
compatible = "pwm-backlight";
|
||||||
|
// Off by default so display power-on won't show the screen from before the last power loss.
|
||||||
|
// The display backlight is turned on during the boot process.
|
||||||
|
status = "disabled";
|
||||||
|
pwm = <&display_backlight_pwm>;
|
||||||
|
};
|
||||||
|
|
||||||
|
touch {
|
||||||
|
compatible = "xptek,xpt2046-softspi";
|
||||||
|
pin-mosi = <&gpio0 32 GPIO_FLAG_NONE>;
|
||||||
|
pin-miso = <&gpio0 39 GPIO_FLAG_NONE>;
|
||||||
|
pin-sck = <&gpio0 25 GPIO_FLAG_NONE>;
|
||||||
|
pin-cs = <&gpio0 33 GPIO_FLAG_NONE>;
|
||||||
|
x-max = <240>;
|
||||||
|
y-max = <320>;
|
||||||
|
mirror-x;
|
||||||
|
};
|
||||||
|
|
||||||
spi0 {
|
spi0 {
|
||||||
compatible = "espressif,esp32-spi";
|
compatible = "espressif,esp32-spi";
|
||||||
host = <SPI2_HOST>;
|
host = <SPI2_HOST>;
|
||||||
cs-gpios = <&gpio0 15 GPIO_FLAG_NONE>, // Display
|
cs-gpios = <&gpio0 15 GPIO_FLAG_NONE>;
|
||||||
<&gpio0 33 GPIO_FLAG_NONE>; // Touch
|
|
||||||
pin-mosi = <&gpio0 13 GPIO_FLAG_NONE>;
|
pin-mosi = <&gpio0 13 GPIO_FLAG_NONE>;
|
||||||
pin-miso = <&gpio0 12 GPIO_FLAG_NONE>;
|
pin-miso = <&gpio0 12 GPIO_FLAG_NONE>;
|
||||||
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
|
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
|
||||||
|
|
||||||
display@0 {
|
display@0 {
|
||||||
compatible = "display-placeholder";
|
compatible = "ilitek,ili9341";
|
||||||
};
|
horizontal-resolution = <240>;
|
||||||
|
vertical-resolution = <320>;
|
||||||
touch@1 {
|
mirror-x;
|
||||||
compatible = "touch-placeholder";
|
bgr-order;
|
||||||
|
pixel-clock-hz = <40000000>;
|
||||||
|
pin-dc = <&gpio0 2 GPIO_FLAG_NONE>;
|
||||||
|
backlight = <&display_backlight>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,22 +1,21 @@
|
|||||||
[general]
|
general.vendor=CYD
|
||||||
vendor=CYD
|
general.name=2432S028R
|
||||||
name=2432S028R
|
|
||||||
|
|
||||||
[apps]
|
apps.launcherAppId=tactility.launcher
|
||||||
launcherAppId=Launcher
|
|
||||||
|
|
||||||
[hardware]
|
hardware.target=ESP32
|
||||||
target=ESP32
|
hardware.flashSize=4MB
|
||||||
flashSize=4MB
|
hardware.spiRam=false
|
||||||
spiRam=false
|
|
||||||
|
|
||||||
[display]
|
storage.userDataLocation=SD
|
||||||
size=2.8"
|
|
||||||
shape=rectangle
|
|
||||||
dpi=143
|
|
||||||
|
|
||||||
[cdn]
|
display.size=2.8"
|
||||||
warningMessage=There are 3 hardware variants of this board. This build works on the original variant only ("v1").
|
display.shape=rectangle
|
||||||
|
display.dpi=143
|
||||||
|
|
||||||
[lvgl]
|
touch.calibrationSupported=true
|
||||||
colorDepth=16
|
touch.calibrationRequired=false
|
||||||
|
|
||||||
|
cdn.warningMessage=There are 3 hardware variants of this board. This build works on the original variant only ("v1").
|
||||||
|
|
||||||
|
lvgl.colorDepth=16
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
- Platforms/platform-esp32
|
- Platforms/platform-esp32
|
||||||
|
- Drivers/ili9341-module
|
||||||
|
- Drivers/xpt2046-softspi-module
|
||||||
dts: cyd,2432s028r.dts
|
dts: cyd,2432s028r.dts
|
||||||
|
|||||||
9
Devices/cyd-2432s028r/source/module.cpp
Normal file
9
Devices/cyd-2432s028r/source/module.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include <tactility/module.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
Module cyd_2432s028r_module = {
|
||||||
|
.name = "cyd-2432s028r"
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS ${SOURCE_FILES}
|
SRCS ${SOURCE_FILES}
|
||||||
INCLUDE_DIRS "Source"
|
REQUIRES TactilityKernel
|
||||||
REQUIRES Tactility esp_lvgl_port ST7789 XPT2046SoftSPI PwmBacklight driver vfs fatfs
|
|
||||||
)
|
)
|
||||||
|
|||||||
195
Devices/cyd-2432s028rv3/LICENSE-Apache-2.0.md
Normal file
195
Devices/cyd-2432s028rv3/LICENSE-Apache-2.0.md
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
Apache License
|
||||||
|
==============
|
||||||
|
|
||||||
|
_Version 2.0, January 2004_
|
||||||
|
_<<http://www.apache.org/licenses/>>_
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
#include "devices/Display.h"
|
|
||||||
#include <driver/gpio.h>
|
|
||||||
|
|
||||||
#include <Tactility/hal/Configuration.h>
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
|
|
||||||
using namespace tt::hal;
|
|
||||||
|
|
||||||
static bool initBoot() {
|
|
||||||
// Set the RGB LED Pins to output and turn them off
|
|
||||||
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT)); // Red
|
|
||||||
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_16, GPIO_MODE_OUTPUT)); // Green
|
|
||||||
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_17, GPIO_MODE_OUTPUT)); // Blue
|
|
||||||
|
|
||||||
// 0 on, 1 off
|
|
||||||
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_4, 1)); // Red
|
|
||||||
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_16, 1)); // Green
|
|
||||||
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_17, 1)); // Blue
|
|
||||||
|
|
||||||
return driver::pwmbacklight::init(LCD_PIN_BACKLIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static DeviceVector createDevices() {
|
|
||||||
return {
|
|
||||||
createDisplay(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extern const Configuration hardwareConfiguration = {
|
|
||||||
.initBoot = initBoot,
|
|
||||||
.createDevices = createDevices
|
|
||||||
};
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user