Skip to content

Commit d6b1d15

Browse files
Various improvements (#614)
- Auto-select widgets in Launcher and apps with toolbars on devices without touch. - Improved USB HID input reliability, cleanup - Updated PSRAM settings to improve boot stability on supported devices. - Prevented duplicate Wi-Fi event subscriptions during screen rebuilds. - Updated docs - Fixes in WifiManage and WifiConnect - Reduced main task stack size - Moved USB HID stack size to PSRAM when available - app_manager_find_manifest() now returns a copy instead of a pointer
1 parent cc8be3f commit d6b1d15

53 files changed

Lines changed: 602 additions & 1088 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.claude/rules.zip‎

10.4 KB
Binary file not shown.

‎.claude/rules/CLAUDE.md‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# CLAUDE.md
2+
3+
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
4+
5+
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
6+
7+
## 1. Think Before Coding
8+
9+
**Don't assume. Don't hide confusion. Surface tradeoffs.**
10+
11+
Before implementing:
12+
- State your assumptions explicitly. If uncertain, ask.
13+
- If multiple interpretations exist, present them - don't pick silently.
14+
- If a simpler approach exists, say so. Push back when warranted.
15+
- If something is unclear, stop. Name what's confusing. Ask.
16+
17+
## 2. Simplicity First
18+
19+
**Minimum code that solves the problem. Nothing speculative.**
20+
21+
- No features beyond what was asked.
22+
- No abstractions for single-use code.
23+
- No "flexibility" or "configurability" that wasn't requested.
24+
- No error handling for impossible scenarios.
25+
- If you write 200 lines and it could be 50, rewrite it.
26+
27+
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
28+
29+
## 3. Surgical Changes
30+
31+
**Touch only what you must. Clean up only your own mess.**
32+
33+
When editing existing code:
34+
- Don't "improve" adjacent code, comments, or formatting.
35+
- Don't refactor things that aren't broken.
36+
- Match existing style, even if you'd do it differently.
37+
- If you notice unrelated dead code, mention it - don't delete it.
38+
39+
When your changes create orphans:
40+
- Remove imports/variables/functions that YOUR changes made unused.
41+
- Don't remove pre-existing dead code unless asked.
42+
43+
The test: Every changed line should trace directly to the user's request.
44+
45+
## 4. Goal-Driven Execution
46+
47+
**Define success criteria. Loop until verified.**
48+
49+
Transform tasks into verifiable goals:
50+
- "Add validation" → "Write tests for invalid inputs, then make them pass"
51+
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
52+
- "Refactor X" → "Ensure tests pass before and after"
53+
54+
For multi-step tasks, state a brief plan:
55+
```
56+
1. [Step] → verify: [check]
57+
2. [Step] → verify: [check]
58+
3. [Step] → verify: [check]
59+
```
60+
61+
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
62+
63+
---
64+
65+
**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.

‎.claude/rules/app-framework.md‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Architecture: App Framework
2+
3+
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.
4+
5+
Lifecycle and inter-app communication go through `app_manager_*()` (`app/manager.h`) and `app_event_*()` (`app/event.h`):
6+
- `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.
7+
- 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).
8+
- An app closes itself by calling `app_manager_finish()` right before returning from `main()`; another instance is closed via `app_manager_stop()`.
9+
10+
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`).
11+
12+
Apps can be loaded from:
13+
14+
- memory (`APP_LOCATION_MEMORY`)
15+
- a path pointing to an install folder where an `.app` file was installed (`APP_LOCATION_PATH`)
16+
- a path pointing to an `.elf` file (`APP_LOCATION_PATH`)
17+
18+
An app can build an optional UI via the LVGL window-manager module (see `lvgl.md`).
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Architecture: Device/Driver/Module System (kernel layer, C API)
2+
3+
The kernel uses a Linux-inspired device model:
4+
5+
- **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.
6+
- **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.
7+
- **Device** (`struct Device`): represents hardware. Lifecycle: `device_construct` → `device_add` → `device_start`. Has a parent-child tree, driver binding, and locking.
8+
- **DeviceType** (`struct DeviceType`): enables discovering devices by category (e.g. `DISPLAY_TYPE`, `TOUCH_TYPE`, `UART_CONTROLLER_TYPE`).
9+
10+
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.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Architecture: Layer Stack (bottom to top)
2+
3+
- **TactilityKernel** — C API kernel: device/driver/module lifecycle, concurrency primitives (thread, mutex, timer, dispatcher), filesystem, logging. Header convention: `<tactility/*.h>` (lowercase snake_case).
4+
- **TactilityFreeRtos** — Thin C++ wrappers around FreeRTOS primitives.
5+
- **Tactility** — Main OS layer: app framework, service framework, LVGL integration, networking and services (Wi-Fi, BLE, NTP, ESP-NOW), settings, i18n.
6+
- **TactilityC** — C bindings (`tt_*.h`) for Tactility, used by side-loaded ELF apps on ESP32. Deprecated, replaced by TactilityKernel.
7+
- **Firmware** — Entry point (`app_main`).

‎.claude/rules/build-system.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Architecture: Build System
2+
3+
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.
4+
5+
`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.).

‎.claude/rules/building.md‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Building
2+
3+
## Git
4+
5+
The repository uses git submodules. Make sure to use `--recurse-submodules` on relevant git commands.
6+
7+
## Simulator (Linux/macOS, no ESP-IDF needed)
8+
9+
> [!IMPORTANT]
10+
> The simulator does **NOT** build or run on native Windows (Win32/PowerShell/cmd). This is
11+
> a hard platform limitation, not a missing tool or PATH issue — do not attempt `cmake -B
12+
> buildsim` on Windows, it will not work. WSL is a separate, Linux environment and is fine.
13+
14+
```bash
15+
cmake -B buildsim -G Ninja
16+
ninja -C buildsim # build firmware + tests
17+
./buildsim/Firmware/Tactility # run simulator
18+
```
19+
20+
## ESP32 firmware
21+
22+
```bash
23+
python device.py <device-id> # generate sdkconfig for device (e.g. lilygo-tdeck)
24+
python device.py <device-id> --dev # dev mode: force 4MB partition table
25+
idf.py build # build firmware
26+
idf.py flash monitor # flash and monitor
27+
```
28+
29+
Device IDs are the folder names under `Devices/` (e.g. `lilygo-tdeck`, `m5stack-cores3`, `cyd-2432s028r`).
30+
31+
### Windows: activating the ESP-IDF environment
32+
33+
On native Windows, `idf.py` is not on PATH by default — it must be activated per-shell first.
34+
The install script places a PowerShell profile activator per IDF version at
35+
`%IDF_TOOLS_PATH%\Microsoft.v<version>.PowerShell_profile.ps1` (path controlled by the
36+
`IDF_TOOLS_PATH` environment variable, set to wherever ESP-IDF's tools were installed, e.g.
37+
`C:\Espressif\tools`). Source it before running any `idf.py` command:
38+
39+
```powershell
40+
. "$env:IDF_TOOLS_PATH\Microsoft.v5.5.2.PowerShell_profile.ps1" # match the installed IDF version
41+
Set-Location "<repo-root>"
42+
idf.py build 2>&1 | Select-Object -Last 250
43+
```
44+
45+
This is Windows-specific setup (the main dev works on Linux, where `idf.py` is normally
46+
already on PATH via `export.sh`/`. ./export.sh` or a shell profile).
47+
48+
## Devicetree
49+
50+
A device implementation has a `.dts` file.
51+
The parser at `Buildscripts/DevicetreeCompiler/` converts DTS into C code.
52+
It's called from the `Firmware/` build process.
53+
54+
## Tests
55+
56+
Tests use Doctest and run on simulator (POSIX) target only:
57+
58+
```bash
59+
cmake -B buildsim -G Ninja
60+
ninja -C buildsim build-tests
61+
cd buildsim && ctest --test-dir Tests
62+
```

‎.claude/rules/coding-style.md‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Coding Style
2+
3+
Two conventions coexist; which one to use depends on the project layer:
4+
5+
- **C code** (TactilityKernel, drivers): `lower_snake_case` for files, functions, variables. `UpperCamelCase` for types. Files in `source/`, `include/`, `private/` directories.
6+
- **C++ code** (Tactility, apps, services): `UpperCamelCase` for files and types. `lowerCamelCase` for functions. Files in `Source/`, `Include/`, `Private/` directories.
7+
8+
For projects that emit C headers and have a C++ implementation file: the internal C++ function naming should be snake_case.
9+
10+
Formatting is enforced by `.clang-format` (LLVM-based, 4-space indent, no column limit).
11+
Never throw exceptions — use return types for error handling. Use `enum class` over plain `enum` when writing C++ code.
12+
Do not add redundant null checks for parameters with an explicit non-null precondition.
13+
14+
Code Comments:
15+
16+
- Should be as short as possible, leaving only important context.
17+
- Should avoid explaining what the code does, unless the code complexity is high enough to warrant an explanation.
18+
- Must avoid explaining how the code was before, or how it was changed.
19+
- Should explain why code is implemented.
20+
- Should be as brief as possible without losing critical information.
21+
- Should avoid explaining what was not implemented.
22+
- Should avoid referring to designs of other subsystems.
23+
- Must avoid interjections: avoid hyphens or braces to interject. If interjections provide crucial info, use Doxygen entity/anchor references like:
24+
/**
25+
* A dedicated completion \signal for one app instance's task.
26+
* Whichever \side finishes with it last is the one that deletes `semaphore` and frees this struct.
27+
*
28+
* \signal Not the task's shared default FreeRTOS notification, which app_event.cpp's AppEventSubscription also uses.
29+
* An unrelated event delivered to the same task could otherwise unblock a waiter early.
30+
* \side The exiting task or a concurrent app_scheduler_stop() that found the entry in time and is waiting on `semaphore`.
31+
*/
32+
```
33+
34+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Architecture: Hardware Abstraction Layer
2+
3+
## Driver
4+
5+
A driver generally consists of:
6+
- Registration of driver in parent module (optional, but desirable)
7+
- YAML bindings in the `bindings/` folder
8+
- An `#include` that is used in the `.dts` file. The include is in `[projectname]/bindings/[drivername].h`
9+
- 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.
10+
11+
Drivers are part of a kernel module.
12+
13+
Modules with drivers can be stored in:
14+
- TactilityKernel
15+
- A subproject in `Platforms` folder
16+
- A subproject in `Devices` folder
17+
- A subproject in `Drivers` folder
18+
19+
## Kernel Modules
20+
21+
Kernel module names are lower case and postfixed with `-module`.
22+
23+
Projects that are kernel modules:
24+
25+
1. Declare a `struct Module`
26+
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:
27+
```yaml
28+
dependencies:
29+
- TactilityKernel
30+
bindings: bindings
31+
```

‎.claude/rules/key-conventions.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Key Conventions
2+
3+
- Shared cross-platform code uses `#ifdef ESP_PLATFORM` for ESP32-specific paths.
4+
Code in `Platforms/PlatformEsp32/` is already ESP-only and does not need guards around ESP-IDF includes.
5+
- The `Drivers/` directory contains hardware drivers (display controllers, touch controllers, PMICs, etc.) — each is its own CMake component.
6+
- `Modules/` contains cross-cutting modules. e.g.`lvgl-module` (LVGL task management).
7+
- `Data/system/` and `Data/data/` are flashed as FAT filesystem images on ESP32.
8+
- Translations are in `Translations/` as CSV files, generated via `generate.py`.

0 commit comments

Comments
 (0)