mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-08-19 16:35:05 +00:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- 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:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- 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"
|
||||
steps:
|
||||
- name: "Checkout repo"
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- 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:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: "Build"
|
||||
uses: ./.github/actions/build-simulator
|
||||
with:
|
||||
@ -20,7 +22,9 @@ jobs:
|
||||
macOS:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: "Build"
|
||||
uses: ./.github/actions/build-simulator
|
||||
with:
|
||||
|
||||
82
.github/workflows/build.yml
vendored
82
.github/workflows/build.yml
vendored
@ -22,66 +22,34 @@ jobs:
|
||||
]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: "Build SDK"
|
||||
uses: ./.github/actions/build-sdk
|
||||
with:
|
||||
board_id: ${{ matrix.board.id }}
|
||||
arch: ${{ matrix.board.arch }}
|
||||
BuildFirmware:
|
||||
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 }
|
||||
]
|
||||
GenerateDeviceMatrix:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ BuildSdk ]
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
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"
|
||||
uses: ./.github/actions/build-firmware
|
||||
with:
|
||||
@ -94,7 +62,9 @@ jobs:
|
||||
(github.event_name == 'push' && github.ref == 'refs/heads/main') ||
|
||||
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v'))
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: "Bundle Artifacts"
|
||||
uses: ./.github/actions/bundle-artifacts
|
||||
PublishFirmwareSnapshot:
|
||||
@ -102,7 +72,7 @@ jobs:
|
||||
needs: [ BundleArtifacts ]
|
||||
if: (github.event_name == 'push' && github.ref == 'refs/heads/main')
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: "Publish Firmware Snapshot"
|
||||
env:
|
||||
CDN_ID: ${{ secrets.CDN_ID }}
|
||||
@ -116,7 +86,7 @@ jobs:
|
||||
needs: [ BundleArtifacts ]
|
||||
if: (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v'))
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: "Publish Firmware Stable"
|
||||
env:
|
||||
CDN_ID: ${{ secrets.CDN_ID }}
|
||||
@ -130,7 +100,7 @@ jobs:
|
||||
needs: [ BundleArtifacts ]
|
||||
if: (github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: "Publish SDKs"
|
||||
env:
|
||||
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
|
||||
steps:
|
||||
- name: "Checkout repo"
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: "Configure Project"
|
||||
@ -19,20 +19,14 @@ jobs:
|
||||
run: cmake -S ./ -B build
|
||||
- name: "Build Tests"
|
||||
run: cmake --build build --target build-tests
|
||||
- name: "Run TactilityCore Tests"
|
||||
run: build/Tests/TactilityCore/TactilityCoreTests
|
||||
- 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
|
||||
- name: "Run Tests"
|
||||
run: ctest --test-dir build/Tests
|
||||
DevicetreeTests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Checkout repo"
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: "Install Python Dependencies"
|
||||
shell: bash
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,9 +1,8 @@
|
||||
.idea/
|
||||
.DS_Store
|
||||
|
||||
build/
|
||||
buildsim/
|
||||
cmake-*/
|
||||
build*/
|
||||
cmake*/
|
||||
CMakeCache.txt
|
||||
*.cbp
|
||||
CMakeFiles
|
||||
@ -25,4 +24,4 @@ sdkconfig.board.*.dev
|
||||
.tactility/
|
||||
|
||||
.caveman.json
|
||||
.ai/mcp
|
||||
.ai/mcp
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -16,3 +16,6 @@
|
||||
[submodule "Libraries/cJSON/cJSON"]
|
||||
path = Libraries/cJSON/cJSON
|
||||
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
|
||||
import os
|
||||
import sys
|
||||
import configparser
|
||||
from dataclasses import dataclass, asdict
|
||||
import json
|
||||
import shutil
|
||||
from configparser import RawConfigParser
|
||||
|
||||
VERBOSE = False
|
||||
DEVICES_FOLDER = "Devices"
|
||||
@ -68,32 +66,29 @@ def exit_with_error(message):
|
||||
sys.exit(1)
|
||||
|
||||
def read_properties_file(path):
|
||||
config = configparser.RawConfigParser()
|
||||
# Don't convert keys to lowercase
|
||||
config.optionxform = str
|
||||
config.read(path)
|
||||
return config
|
||||
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_property_or_none(properties: RawConfigParser, group: str, key: str):
|
||||
if group not in properties.sections():
|
||||
return None
|
||||
if key not in properties[group].keys():
|
||||
return None
|
||||
return properties[group][key]
|
||||
def get_property_or_none(properties: dict, group: str, key: str):
|
||||
return properties.get(f"{group}.{key}")
|
||||
|
||||
def get_boolean_property_or_false(properties: RawConfigParser, group: str, key: str):
|
||||
if group not in properties.sections():
|
||||
return False
|
||||
if key not in properties[group].keys():
|
||||
return False
|
||||
return properties[group][key] == "true"
|
||||
def get_boolean_property_or_false(properties: dict, group: str, key: str):
|
||||
return properties.get(f"{group}.{key}") == "true"
|
||||
|
||||
def get_property_or_exit(properties: RawConfigParser, group: str, key: str):
|
||||
if group not in properties.sections():
|
||||
exit_with_error(f"Device properties does not contain group: {group}")
|
||||
if key not in properties[group].keys():
|
||||
exit_with_error(f"Device properties does not contain key: {key}")
|
||||
return properties[group][key]
|
||||
def get_property_or_exit(properties: dict, group: str, key: str):
|
||||
full_key = f"{group}.{key}"
|
||||
if full_key not in properties:
|
||||
exit_with_error(f"Device properties does not contain key: {full_key}")
|
||||
return properties[full_key]
|
||||
|
||||
def read_device_properties(device_id):
|
||||
mapping_file_path = os.path.join(DEVICES_FOLDER, device_id, "device.properties")
|
||||
@ -129,7 +124,7 @@ def to_manifest_chip_name(name):
|
||||
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_binaries_path = os.path.join(in_device_path, "Binaries")
|
||||
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(),
|
||||
default=details.get('default', None),
|
||||
element_type=details.get('element-type', None),
|
||||
min=details.get('min', None),
|
||||
max=details.get('max', None),
|
||||
)
|
||||
properties_dict[name] = prop
|
||||
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]):
|
||||
device_binding = find_device_binding(device, bindings)
|
||||
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:
|
||||
raise DevicetreeException(f"Couldn't find compatible binding for {device.node_name}")
|
||||
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))
|
||||
return "{ " + ",".join(value_list) + " }"
|
||||
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)
|
||||
elif type == "phandles":
|
||||
value_list = list()
|
||||
@ -113,6 +118,25 @@ def resolve_phandle_array_entries(device_property, devices):
|
||||
entries.append(str(item))
|
||||
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:
|
||||
compatible_property = find_device_property(device, "compatible")
|
||||
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)
|
||||
result = []
|
||||
phandle_arrays = []
|
||||
array_decls = []
|
||||
for binding_property in binding_properties:
|
||||
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}"
|
||||
if device_property is not None:
|
||||
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(str(len(entries)))
|
||||
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 binding_property.default is not None:
|
||||
validate_property_range(device, binding_property, binding_property.default)
|
||||
temp_prop = DeviceProperty(
|
||||
name=binding_property.name,
|
||||
type=binding_property.type,
|
||||
@ -179,19 +232,20 @@ def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], de
|
||||
else:
|
||||
raise DevicetreeException(f"Device {device.node_name} doesn't have property '{binding_property.name}' and no default value is set")
|
||||
else:
|
||||
validate_property_range(device, binding_property, device_property.value)
|
||||
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):
|
||||
node_name = get_device_node_name_safe(device)
|
||||
config_type = f"{type_name}_config_dt"
|
||||
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
|
||||
for array_var, element_type, entries in phandle_arrays:
|
||||
# Write phandle-array/array variables before the config struct
|
||||
for array_var, element_type, entries in array_decls:
|
||||
entries_str = ", ".join(entries)
|
||||
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.name = \"{device.node_name}\",\n") # Use original name
|
||||
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("\t.internal = NULL\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:
|
||||
if type(item) is Device:
|
||||
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:
|
||||
if type(item) is Device:
|
||||
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("\n")
|
||||
# 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:
|
||||
file.write(f"\t&{symbol},\n")
|
||||
file.write("\tNULL\n")
|
||||
@ -342,10 +397,10 @@ def generate_devicetree_h(filename: str):
|
||||
#endif
|
||||
|
||||
// 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
|
||||
extern struct Module* dts_modules[];
|
||||
extern struct Module* const dts_modules[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -40,6 +40,8 @@ class BindingProperty:
|
||||
description: str
|
||||
default: object = None
|
||||
element_type: str = None
|
||||
min: object = None
|
||||
max: object = None
|
||||
|
||||
@dataclass
|
||||
class Binding:
|
||||
|
||||
@ -13,6 +13,7 @@ static struct Device root = {
|
||||
.address = 0,
|
||||
.name = "/",
|
||||
.config = &root_config,
|
||||
.flags = DEVICE_FLAG_DTS,
|
||||
.parent = NULL,
|
||||
.internal = NULL
|
||||
};
|
||||
@ -27,6 +28,7 @@ static struct Device test_device = {
|
||||
.address = 0,
|
||||
.name = "test-device",
|
||||
.config = &test_device_config,
|
||||
.flags = DEVICE_FLAG_DTS,
|
||||
.parent = &root,
|
||||
.internal = NULL
|
||||
};
|
||||
@ -43,11 +45,12 @@ static struct Device bool_test_device = {
|
||||
.address = 0,
|
||||
.name = "bool-test-device",
|
||||
.config = &bool_test_device_config,
|
||||
.flags = DEVICE_FLAG_DTS,
|
||||
.parent = &root,
|
||||
.internal = NULL
|
||||
};
|
||||
|
||||
struct DtsDevice dts_devices[] = {
|
||||
const struct DtsDevice dts_devices[] = {
|
||||
{ &root, "test,root", DTS_DEVICE_STATUS_OKAY },
|
||||
{ &test_device, "test,generic-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;
|
||||
|
||||
struct Module* dts_modules[] = {
|
||||
struct Module* const dts_modules[] = {
|
||||
&data_module,
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -7,10 +7,10 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
// 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
|
||||
extern struct Module* dts_modules[];
|
||||
extern struct Module* const dts_modules[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -76,6 +76,225 @@ def test_compile_invalid_dts():
|
||||
print("PASSED")
|
||||
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():
|
||||
print("Running test_compile_missing_config...")
|
||||
with tempfile.TemporaryDirectory() as output_dir:
|
||||
@ -96,7 +315,14 @@ if __name__ == "__main__":
|
||||
tests = [
|
||||
test_compile_success,
|
||||
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
|
||||
|
||||
@ -13,9 +13,9 @@ $jsonClean = $json.flash_files -replace '[\{\}\@\;]', ''
|
||||
$jsonClean = $jsonClean -replace '[\=]', ' '
|
||||
|
||||
cd Binaries
|
||||
$command = "esptool --port $port erase_flash"
|
||||
$command = "esptool --port $port erase-flash"
|
||||
Invoke-Expression $command
|
||||
$command = "esptool --port $port write_flash $jsonClean"
|
||||
$command = "esptool --port $port write-flash $jsonClean"
|
||||
Invoke-Expression $command
|
||||
cd ..
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ fi
|
||||
# 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=' '`
|
||||
cd Binaries
|
||||
$esptoolPath --port $1 erase_flash
|
||||
$esptoolPath --port $1 write_flash $flash_args
|
||||
$esptoolPath --port $1 erase-flash
|
||||
$esptoolPath --port $1 write-flash $flash_args
|
||||
cd -
|
||||
|
||||
|
||||
@ -2,23 +2,23 @@ idf_component_register(
|
||||
INCLUDE_DIRS
|
||||
"Libraries/TactilityC/include"
|
||||
"Libraries/TactilityKernel/include"
|
||||
"Libraries/TactilityFreeRtos/include"
|
||||
"Libraries/TactilityFreeRtos/Include"
|
||||
"Libraries/lvgl/include"
|
||||
"Libraries/minmea/include"
|
||||
"Libraries/minitar/include"
|
||||
"Modules/lvgl-module/include"
|
||||
"Drivers/bm8563-module/include"
|
||||
"Drivers/bmi270-module/include"
|
||||
"Drivers/mpu6886-module/include"
|
||||
"Drivers/pi4ioe5v6408-module/include"
|
||||
"Drivers/qmi8658-module/include"
|
||||
"Drivers/rx8130ce-module/include"
|
||||
REQUIRES esp_timer
|
||||
REQUIRES esp_timer app-module crypt-module gps-module lvgl-module lvgl-window-manager-module service-module
|
||||
)
|
||||
|
||||
# Regular and core features
|
||||
add_prebuilt_library(TactilityC Libraries/TactilityC/binary/libTactilityC.a)
|
||||
add_prebuilt_library(TactilityKernel Libraries/TactilityKernel/binary/libTactilityKernel.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 TactilityKernel)
|
||||
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()
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
"Libraries/TactilityFreeRtos"
|
||||
"Modules"
|
||||
"Drivers"
|
||||
"${TACTILITY_SDK_PATH}/Libraries/TactilityFreeRtos"
|
||||
"${TACTILITY_SDK_PATH}/Modules"
|
||||
)
|
||||
|
||||
set(COMPONENTS
|
||||
TactilityFreeRtos
|
||||
bm8563-module
|
||||
bmi270-module
|
||||
mpu6886-module
|
||||
pi4ioe5v6408-module
|
||||
qmi8658-module
|
||||
rx8130ce-module
|
||||
app-module
|
||||
crypt-module
|
||||
gps-module
|
||||
lvgl-module
|
||||
lvgl-window-manager-module
|
||||
service-module
|
||||
)
|
||||
|
||||
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)
|
||||
# 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
|
||||
# Supports KEY=VALUE, KEY="VALUE", and optional spaces around =
|
||||
# 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.
|
||||
if ("${${PROPERTIES_CONTENT_VAR}}" MATCHES "(^|\n)${KEY_NAME}[ \t]*=[ \t]*\"?([^\n\"]*)\"?")
|
||||
set(${RESULT_VAR} "${CMAKE_MATCH_2}" PARENT_SCOPE)
|
||||
elseif (has_default)
|
||||
set(${RESULT_VAR} "${default_value}" PARENT_SCOPE)
|
||||
else ()
|
||||
message(FATAL_ERROR "Property '${KEY_NAME}' not found in the properties content.")
|
||||
endif ()
|
||||
@ -28,7 +37,6 @@ function(READ_PROPERTIES_TO_MAP PROPERTY_FILE RESULT_VAR)
|
||||
endif ()
|
||||
|
||||
file(STRINGS ${PROPERTY_FILE_ABS} lines)
|
||||
set(current_section "")
|
||||
set(map_content "")
|
||||
|
||||
foreach(line IN LISTS lines)
|
||||
@ -37,9 +45,7 @@ function(READ_PROPERTIES_TO_MAP PROPERTY_FILE RESULT_VAR)
|
||||
continue()
|
||||
endif ()
|
||||
|
||||
if (line MATCHES "^\\[.*\\]$")
|
||||
set(current_section "${line}")
|
||||
elseif (line MATCHES "^([^=]+)=(.*)$")
|
||||
if (line MATCHES "^([^=]+)=(.*)$")
|
||||
set(key "${CMAKE_MATCH_1}")
|
||||
set(value "${CMAKE_MATCH_2}")
|
||||
string(STRIP "${key}" key)
|
||||
@ -49,7 +55,7 @@ function(READ_PROPERTIES_TO_MAP PROPERTY_FILE RESULT_VAR)
|
||||
set(value "${CMAKE_MATCH_1}")
|
||||
endif ()
|
||||
|
||||
list(APPEND map_content "${current_section}${key}" "${value}")
|
||||
list(APPEND map_content "${key}" "${value}")
|
||||
endif ()
|
||||
endforeach()
|
||||
|
||||
|
||||
@ -88,6 +88,17 @@ def write_module_cmakelists(path, content):
|
||||
with open(path, 'w') as f:
|
||||
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):
|
||||
mappings = get_driver_mappings(driver_name)
|
||||
map_copy(mappings, target_path)
|
||||
@ -100,6 +111,14 @@ def add_module(target_path, 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)
|
||||
|
||||
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():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: release-sdk.py [target_path]")
|
||||
@ -118,7 +137,7 @@ def main():
|
||||
{'src': 'TactilityC/CMakeLists.txt', 'dst': 'Libraries/TactilityC/'},
|
||||
{'src': 'TactilityC/LICENSE*.*', 'dst': 'Libraries/TactilityC/'},
|
||||
# TactilityFreeRtos
|
||||
{'src': 'TactilityFreeRtos/Include/**', 'dst': 'Libraries/TactilityFreeRtos/include/'},
|
||||
{'src': 'TactilityFreeRtos/Include/**', 'dst': 'Libraries/TactilityFreeRtos/Include/'},
|
||||
{'src': 'TactilityFreeRtos/CMakeLists.txt', 'dst': 'Libraries/TactilityFreeRtos/'},
|
||||
{'src': 'TactilityFreeRtos/LICENSE*.*', 'dst': 'Libraries/TactilityFreeRtos/'},
|
||||
# TactilityKernel
|
||||
@ -136,23 +155,32 @@ def main():
|
||||
# elf_loader
|
||||
{'src': 'Libraries/elf_loader/elf_loader.cmake', 'dst': 'Libraries/elf_loader/'},
|
||||
{'src': 'Libraries/elf_loader/license.txt', 'dst': 'Libraries/elf_loader/'},
|
||||
# Final scripts
|
||||
{'src': 'Buildscripts/TactilitySDK/TactilitySDK.cmake', 'dst': ''},
|
||||
{'src': 'Buildscripts/TactilitySDK/CMakeLists.txt', 'dst': ''},
|
||||
# minitar
|
||||
{'src': 'build/esp-idf/minitar/libminitar.a', 'dst': 'Libraries/minitar/binary/'},
|
||||
{'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)
|
||||
|
||||
# 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-window-manager-module")
|
||||
add_module(target_path, "service-module")
|
||||
|
||||
# Drivers
|
||||
add_driver(target_path, "bm8563-module")
|
||||
add_driver(target_path, "bmi270-module")
|
||||
add_driver(target_path, "mpu6886-module")
|
||||
add_driver(target_path, "pi4ioe5v6408-module")
|
||||
add_driver(target_path, "qmi8658-module")
|
||||
add_driver(target_path, "rx8130ce-module")
|
||||
# Final scripts - copied verbatim
|
||||
generate_tactility_sdk_cmake(target_path)
|
||||
generate_tactility_sdk_top_cmakelists(target_path)
|
||||
|
||||
# Output ESP-IDF SDK version to file
|
||||
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,7 +1,7 @@
|
||||
# Increase stack size for Wi-Fi (fixes crash after scan)
|
||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3072
|
||||
# Ensure large enough stack for network operations
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=6144
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096
|
||||
# Fixes static assertion: FLASH and PSRAM Mode configuration are not supported
|
||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
||||
# Free up IRAM
|
||||
@ -26,6 +26,9 @@ CONFIG_LV_USE_WIN=n
|
||||
CONFIG_LV_USE_SNAPSHOT=y
|
||||
CONFIG_LV_BUILD_EXAMPLES=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_TASK_NOTIFICATION_ARRAY_ENTRIES=2
|
||||
CONFIG_FREERTOS_SMP=n
|
||||
@ -41,6 +44,11 @@ CONFIG_WL_SECTOR_MODE_SAFE=y
|
||||
CONFIG_WL_SECTOR_MODE=1
|
||||
# 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
|
||||
# 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
|
||||
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=3
|
||||
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=5
|
||||
|
||||
@ -41,11 +41,12 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
"Modules"
|
||||
"Platforms/platform-esp32"
|
||||
"TactilityKernel"
|
||||
"TactilityKernelCpp"
|
||||
"Tactility"
|
||||
"TactilityC"
|
||||
"TactilityCore"
|
||||
"TactilityFreeRtos"
|
||||
"Libraries/elf_loader"
|
||||
"Libraries/esp_epaper"
|
||||
"Libraries/lv_screenshot"
|
||||
"Libraries/minitar"
|
||||
"Libraries/minmea"
|
||||
@ -70,12 +71,16 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
|
||||
else ()
|
||||
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_NAME="Simulator")
|
||||
add_compile_definitions(CONFIG_TT_DEVICE_VENDOR="")
|
||||
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_USER_DATA_LOCATION_INTERNAL)
|
||||
endif ()
|
||||
|
||||
project(Tactility)
|
||||
@ -83,9 +88,9 @@ project(Tactility)
|
||||
# Defined as regular project for PC and component for ESP
|
||||
if (NOT DEFINED ENV{ESP_IDF_VERSION})
|
||||
add_subdirectory(Tactility)
|
||||
add_subdirectory(TactilityCore)
|
||||
add_subdirectory(TactilityFreeRtos)
|
||||
add_subdirectory(TactilityKernel)
|
||||
add_subdirectory(TactilityKernelCpp)
|
||||
add_subdirectory(Platforms/platform-posix)
|
||||
add_subdirectory(Devices/simulator)
|
||||
add_subdirectory(Libraries/cJSON)
|
||||
@ -93,8 +98,14 @@ if (NOT DEFINED ENV{ESP_IDF_VERSION})
|
||||
add_subdirectory(Libraries/QRCode)
|
||||
add_subdirectory(Libraries/minitar)
|
||||
add_subdirectory(Libraries/minmea)
|
||||
add_subdirectory(Modules/hal-device-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
|
||||
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>
|
||||
</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>
|
||||
<div class="toolbar-sep"></div>
|
||||
<div class="toolbar-group">
|
||||
@ -739,7 +739,6 @@ function renderDashboard(data) {
|
||||
<div class="card">
|
||||
<h2>Quick Actions</h2>
|
||||
<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>' : ''}
|
||||
<button class="btn btn-danger" onclick="rebootDevice(this)">Reboot</button>
|
||||
</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(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility esp_lvgl_port esp_lcd RgbDisplay GT911 PwmBacklight driver vfs fatfs
|
||||
INCLUDE_DIRS "source"
|
||||
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/esp32_ble.h>
|
||||
#include <tactility/bindings/esp32_wifi_pinned.h>
|
||||
#include <tactility/bindings/esp32_gpio.h>
|
||||
#include <tactility/bindings/esp32_i2c.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
|
||||
/ {
|
||||
compatible = "root";
|
||||
model = "BigTreeTech Panda Touch";
|
||||
|
||||
wifi0 {
|
||||
compatible = "espressif,esp32-wifi-pinned";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ble0 {
|
||||
compatible = "espressif,esp32-ble";
|
||||
status = "disabled";
|
||||
@ -27,6 +37,15 @@
|
||||
clock-frequency = <400000>;
|
||||
pin-sda = <&gpio0 2 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 {
|
||||
@ -37,6 +56,57 @@
|
||||
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 {
|
||||
compatible = "espressif,esp32-usbhost";
|
||||
|
||||
|
||||
@ -1,25 +1,22 @@
|
||||
[general]
|
||||
vendor=BigTreeTech
|
||||
name=Panda Touch,K Touch
|
||||
general.vendor=BigTreeTech
|
||||
general.name=Panda Touch,K Touch
|
||||
|
||||
[apps]
|
||||
launcherAppId=Launcher
|
||||
apps.launcherAppId=tactility.launcher
|
||||
|
||||
[hardware]
|
||||
target=ESP32S3
|
||||
flashSize=16MB
|
||||
spiRam=true
|
||||
spiRamMode=OCT
|
||||
spiRamSpeed=120M
|
||||
esptoolFlashFreq=120M
|
||||
bluetooth=true
|
||||
usbHostEnabled=true
|
||||
hardware.target=ESP32S3
|
||||
hardware.flashSize=16MB
|
||||
hardware.spiRam=true
|
||||
hardware.spiRamMode=OCT
|
||||
hardware.spiRamSpeed=120M
|
||||
hardware.esptoolFlashFreq=120M
|
||||
hardware.bluetooth=true
|
||||
hardware.usbHostEnabled=true
|
||||
|
||||
[display]
|
||||
size=5"
|
||||
shape=rectangle
|
||||
dpi=187
|
||||
storage.userDataLocation=Internal
|
||||
|
||||
[lvgl]
|
||||
colorDepth=16
|
||||
fontSize=18
|
||||
display.size=5"
|
||||
display.shape=rectangle
|
||||
display.dpi=187
|
||||
|
||||
lvgl.colorDepth=16
|
||||
lvgl.fontSize=18
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/rgb-display-module
|
||||
- Drivers/gt911-module
|
||||
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(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility esp_lvgl_port ILI934x CST816S PwmBacklight driver vfs fatfs
|
||||
REQUIRES TactilityKernel
|
||||
)
|
||||
|
||||
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/;
|
||||
|
||||
#include <tactility/bindings/root.h>
|
||||
#include <tactility/bindings/esp32_wifi_pinned.h>
|
||||
#include <tactility/bindings/esp32_gpio.h>
|
||||
#include <tactility/bindings/esp32_i2c.h>
|
||||
#include <tactility/bindings/esp32_spi.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";
|
||||
model = "CYD 2432S024C";
|
||||
|
||||
wifi0 {
|
||||
compatible = "espressif,esp32-wifi-pinned";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
gpio0 {
|
||||
compatible = "espressif,esp32-gpio";
|
||||
gpio-count = <40>;
|
||||
@ -22,6 +32,66 @@
|
||||
clock-frequency = <400000>;
|
||||
pin-sda = <&gpio0 33 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 {
|
||||
@ -30,9 +100,16 @@
|
||||
cs-gpios = <&gpio0 15 GPIO_FLAG_NONE>;
|
||||
pin-mosi = <&gpio0 13 GPIO_FLAG_NONE>;
|
||||
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
|
||||
|
||||
display {
|
||||
compatible = "display-placeholder";
|
||||
|
||||
display@0 {
|
||||
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,7 +120,8 @@
|
||||
pin-mosi = <&gpio0 23 GPIO_FLAG_NONE>;
|
||||
pin-miso = <&gpio0 19 GPIO_FLAG_NONE>;
|
||||
pin-sclk = <&gpio0 18 GPIO_FLAG_NONE>;
|
||||
|
||||
miso-pull-up;
|
||||
|
||||
sdcard@0 {
|
||||
compatible = "espressif,esp32-sdspi";
|
||||
frequency-khz = <20000>;
|
||||
|
||||
@ -1,19 +1,16 @@
|
||||
[general]
|
||||
vendor=CYD
|
||||
name=2432S024C
|
||||
general.vendor=CYD
|
||||
general.name=2432S024C
|
||||
|
||||
[apps]
|
||||
launcherAppId=Launcher
|
||||
apps.launcherAppId=tactility.launcher
|
||||
|
||||
[hardware]
|
||||
target=ESP32
|
||||
flashSize=4MB
|
||||
spiRam=false
|
||||
hardware.target=ESP32
|
||||
hardware.flashSize=4MB
|
||||
hardware.spiRam=false
|
||||
|
||||
[display]
|
||||
size=2.4"
|
||||
shape=rectangle
|
||||
dpi=167
|
||||
storage.userDataLocation=SD
|
||||
|
||||
[lvgl]
|
||||
colorDepth=16
|
||||
display.size=2.4"
|
||||
display.shape=rectangle
|
||||
display.dpi=167
|
||||
|
||||
lvgl.colorDepth=16
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/ili9341-module
|
||||
- Drivers/cst816s-module
|
||||
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(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility esp_lvgl_port ILI934x XPT2046 PwmBacklight driver vfs fatfs
|
||||
REQUIRES TactilityKernel
|
||||
)
|
||||
|
||||
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/;
|
||||
|
||||
#include <tactility/bindings/root.h>
|
||||
#include <tactility/bindings/esp32_wifi_pinned.h>
|
||||
#include <tactility/bindings/esp32_gpio.h>
|
||||
#include <tactility/bindings/esp32_spi.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_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";
|
||||
model = "CYD 2432S024R";
|
||||
|
||||
wifi0 {
|
||||
compatible = "espressif,esp32-wifi-pinned";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
gpio0 {
|
||||
compatible = "espressif,esp32-gpio";
|
||||
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 {
|
||||
compatible = "espressif,esp32-spi";
|
||||
host = <SPI2_HOST>;
|
||||
@ -27,11 +89,23 @@
|
||||
<&gpio0 33 GPIO_FLAG_NONE>; // Touch
|
||||
|
||||
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 {
|
||||
compatible = "touch-placeholder";
|
||||
compatible = "xptek,xpt2046";
|
||||
x-max = <240>;
|
||||
y-max = <320>;
|
||||
swap-xy;
|
||||
mirror-y;
|
||||
};
|
||||
};
|
||||
|
||||
@ -55,4 +129,4 @@
|
||||
pin-tx = <&gpio0 1 GPIO_FLAG_NONE>;
|
||||
pin-rx = <&gpio0 3 GPIO_FLAG_NONE>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,19 +1,16 @@
|
||||
[general]
|
||||
vendor=CYD
|
||||
name=2432S024R
|
||||
general.vendor=CYD
|
||||
general.name=2432S024R
|
||||
|
||||
[apps]
|
||||
launcherAppId=Launcher
|
||||
apps.launcherAppId=tactility.launcher
|
||||
|
||||
[hardware]
|
||||
target=ESP32
|
||||
flashSize=4MB
|
||||
spiRam=false
|
||||
hardware.target=ESP32
|
||||
hardware.flashSize=4MB
|
||||
hardware.spiRam=false
|
||||
|
||||
[display]
|
||||
size=2.4"
|
||||
shape=rectangle
|
||||
dpi=167
|
||||
storage.userDataLocation=SD
|
||||
|
||||
[lvgl]
|
||||
colorDepth=16
|
||||
display.size=2.4"
|
||||
display.shape=rectangle
|
||||
display.dpi=167
|
||||
|
||||
lvgl.colorDepth=16
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/ili9341-module
|
||||
- Drivers/xpt2046-module
|
||||
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(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility esp_lvgl_port ILI934x XPT2046SoftSPI PwmBacklight driver vfs fatfs
|
||||
REQUIRES TactilityKernel
|
||||
)
|
||||
|
||||
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/;
|
||||
|
||||
#include <tactility/bindings/root.h>
|
||||
#include <tactility/bindings/esp32_wifi_pinned.h>
|
||||
#include <tactility/bindings/esp32_gpio.h>
|
||||
#include <tactility/bindings/esp32_i2c.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <tactility/bindings/esp32_uart.h>
|
||||
#include <tactility/bindings/esp32_sdspi.h>
|
||||
#include <tactility/bindings/display_placeholder.h>
|
||||
#include <tactility/bindings/touch_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/xpt2046_softspi.h>
|
||||
|
||||
/ {
|
||||
compatible = "root";
|
||||
model = "CYD 2432S028R";
|
||||
|
||||
wifi0 {
|
||||
compatible = "espressif,esp32-wifi-pinned";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
gpio0 {
|
||||
compatible = "espressif,esp32-gpio";
|
||||
gpio-count = <40>;
|
||||
@ -26,21 +35,87 @@
|
||||
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 {
|
||||
compatible = "espressif,esp32-spi";
|
||||
host = <SPI2_HOST>;
|
||||
cs-gpios = <&gpio0 15 GPIO_FLAG_NONE>, // Display
|
||||
<&gpio0 33 GPIO_FLAG_NONE>; // Touch
|
||||
cs-gpios = <&gpio0 15 GPIO_FLAG_NONE>;
|
||||
pin-mosi = <&gpio0 13 GPIO_FLAG_NONE>;
|
||||
pin-miso = <&gpio0 12 GPIO_FLAG_NONE>;
|
||||
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
|
||||
|
||||
display@0 {
|
||||
compatible = "display-placeholder";
|
||||
};
|
||||
|
||||
touch@1 {
|
||||
compatible = "touch-placeholder";
|
||||
display@0 {
|
||||
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>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -1,22 +1,21 @@
|
||||
[general]
|
||||
vendor=CYD
|
||||
name=2432S028R
|
||||
general.vendor=CYD
|
||||
general.name=2432S028R
|
||||
|
||||
[apps]
|
||||
launcherAppId=Launcher
|
||||
apps.launcherAppId=tactility.launcher
|
||||
|
||||
[hardware]
|
||||
target=ESP32
|
||||
flashSize=4MB
|
||||
spiRam=false
|
||||
hardware.target=ESP32
|
||||
hardware.flashSize=4MB
|
||||
hardware.spiRam=false
|
||||
|
||||
[display]
|
||||
size=2.8"
|
||||
shape=rectangle
|
||||
dpi=143
|
||||
storage.userDataLocation=SD
|
||||
|
||||
[cdn]
|
||||
warningMessage=There are 3 hardware variants of this board. This build works on the original variant only ("v1").
|
||||
display.size=2.8"
|
||||
display.shape=rectangle
|
||||
display.dpi=143
|
||||
|
||||
[lvgl]
|
||||
colorDepth=16
|
||||
touch.calibrationSupported=true
|
||||
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:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/ili9341-module
|
||||
- Drivers/xpt2046-softspi-module
|
||||
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(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility esp_lvgl_port ST7789 XPT2046SoftSPI PwmBacklight driver vfs fatfs
|
||||
REQUIRES TactilityKernel
|
||||
)
|
||||
|
||||
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