Compare commits

..

1 Commits

Author SHA1 Message Date
Adolfo Reyna 99ac246ab5 Associate .mp3 files with the Mp3Player application in Files browser 2026-06-28 22:24:45 -04:00
4464 changed files with 2157 additions and 936632 deletions
-4
View File
@@ -11,8 +11,6 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Build" - name: "Build"
uses: ./.github/actions/build-simulator uses: ./.github/actions/build-simulator
with: with:
@@ -23,8 +21,6 @@ jobs:
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Build" - name: "Build"
uses: ./.github/actions/build-simulator uses: ./.github/actions/build-simulator
with: with:
+1 -4
View File
@@ -23,8 +23,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Build SDK" - name: "Build SDK"
uses: ./.github/actions/build-sdk uses: ./.github/actions/build-sdk
with: with:
@@ -68,6 +66,7 @@ jobs:
{ id: m5stack-cores3, arch: esp32s3 }, { id: m5stack-cores3, arch: esp32s3 },
{ id: m5stack-papers3, arch: esp32s3 }, { id: m5stack-papers3, arch: esp32s3 },
{ id: m5stack-stackchan, arch: esp32s3 }, { id: m5stack-stackchan, arch: esp32s3 },
{ id: m5stack-stickc-plus, arch: esp32 },
{ id: m5stack-stickc-plus2, arch: esp32 }, { id: m5stack-stickc-plus2, arch: esp32 },
{ id: m5stack-sticks3, arch: esp32s3 }, { id: m5stack-sticks3, arch: esp32s3 },
{ id: m5stack-tab5, arch: esp32p4 }, { id: m5stack-tab5, arch: esp32p4 },
@@ -83,8 +82,6 @@ jobs:
needs: [ BuildSdk ] needs: [ BuildSdk ]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Build Firmware" - name: "Build Firmware"
uses: ./.github/actions/build-firmware uses: ./.github/actions/build-firmware
with: with:
+1 -2
View File
@@ -31,9 +31,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: "Checkout repo" - name: "Checkout repo"
- uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
persist-credentials: false
submodules: recursive submodules: recursive
- name: "Install Python Dependencies" - name: "Install Python Dependencies"
shell: bash shell: bash
+26 -21
View File
@@ -2,9 +2,11 @@ import subprocess
from datetime import datetime, UTC from datetime import datetime, UTC
import os import os
import sys import sys
import configparser
from dataclasses import dataclass, asdict from dataclasses import dataclass, asdict
import json import json
import shutil import shutil
from configparser import RawConfigParser
VERBOSE = False VERBOSE = False
DEVICES_FOLDER = "Devices" DEVICES_FOLDER = "Devices"
@@ -66,29 +68,32 @@ def exit_with_error(message):
sys.exit(1) sys.exit(1)
def read_properties_file(path): def read_properties_file(path):
properties = {} config = configparser.RawConfigParser()
with open(path, "r") as file: # Don't convert keys to lowercase
for line in file: config.optionxform = str
stripped = line.strip() config.read(path)
if not stripped or stripped.startswith("#"): return config
continue
key, sep, value = line.partition("=")
if not sep:
continue
properties[key.strip()] = value.strip()
return properties
def get_property_or_none(properties: dict, group: str, key: str): def get_property_or_none(properties: RawConfigParser, group: str, key: str):
return properties.get(f"{group}.{key}") if group not in properties.sections():
return None
if key not in properties[group].keys():
return None
return properties[group][key]
def get_boolean_property_or_false(properties: dict, group: str, key: str): def get_boolean_property_or_false(properties: RawConfigParser, group: str, key: str):
return properties.get(f"{group}.{key}") == "true" if group not in properties.sections():
return False
if key not in properties[group].keys():
return False
return properties[group][key] == "true"
def get_property_or_exit(properties: dict, group: str, key: str): def get_property_or_exit(properties: RawConfigParser, group: str, key: str):
full_key = f"{group}.{key}" if group not in properties.sections():
if full_key not in properties: exit_with_error(f"Device properties does not contain group: {group}")
exit_with_error(f"Device properties does not contain key: {full_key}") if key not in properties[group].keys():
return properties[full_key] exit_with_error(f"Device properties does not contain key: {key}")
return properties[group][key]
def read_device_properties(device_id): def read_device_properties(device_id):
mapping_file_path = os.path.join(DEVICES_FOLDER, device_id, "device.properties") mapping_file_path = os.path.join(DEVICES_FOLDER, device_id, "device.properties")
@@ -124,7 +129,7 @@ def to_manifest_chip_name(name):
return "" return ""
def process_device(in_path: str, out_path: str, device_directory: str, device_id: str, device_properties: dict, version: str): def process_device(in_path: str, out_path: str, device_directory: str, device_id: str, device_properties: RawConfigParser, version: str):
in_device_path = os.path.join(in_path, device_directory) in_device_path = os.path.join(in_path, device_directory)
in_device_binaries_path = os.path.join(in_device_path, "Binaries") in_device_binaries_path = os.path.join(in_device_path, "Binaries")
if not os.path.isdir(in_device_binaries_path): if not os.path.isdir(in_device_binaries_path):
-135
View File
@@ -1,135 +0,0 @@
#!/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()
-126
View File
@@ -1,126 +0,0 @@
#!/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()
+5 -2
View File
@@ -28,6 +28,7 @@ function(READ_PROPERTIES_TO_MAP PROPERTY_FILE RESULT_VAR)
endif () endif ()
file(STRINGS ${PROPERTY_FILE_ABS} lines) file(STRINGS ${PROPERTY_FILE_ABS} lines)
set(current_section "")
set(map_content "") set(map_content "")
foreach(line IN LISTS lines) foreach(line IN LISTS lines)
@@ -36,7 +37,9 @@ function(READ_PROPERTIES_TO_MAP PROPERTY_FILE RESULT_VAR)
continue() continue()
endif () endif ()
if (line MATCHES "^([^=]+)=(.*)$") if (line MATCHES "^\\[.*\\]$")
set(current_section "${line}")
elseif (line MATCHES "^([^=]+)=(.*)$")
set(key "${CMAKE_MATCH_1}") set(key "${CMAKE_MATCH_1}")
set(value "${CMAKE_MATCH_2}") set(value "${CMAKE_MATCH_2}")
string(STRIP "${key}" key) string(STRIP "${key}" key)
@@ -46,7 +49,7 @@ function(READ_PROPERTIES_TO_MAP PROPERTY_FILE RESULT_VAR)
set(value "${CMAKE_MATCH_1}") set(value "${CMAKE_MATCH_1}")
endif () endif ()
list(APPEND map_content "${key}" "${value}") list(APPEND map_content "${current_section}${key}" "${value}")
endif () endif ()
endforeach() endforeach()
-84
View File
@@ -1,84 +0,0 @@
#!/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 -18
View File
@@ -2,8 +2,6 @@
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3072 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3072
# Ensure large enough stack for network operations # Ensure large enough stack for network operations
CONFIG_ESP_MAIN_TASK_STACK_SIZE=6144 CONFIG_ESP_MAIN_TASK_STACK_SIZE=6144
# HTTP server: need enough URI handler slots for WebServer (7 + internal) + DevServer
CONFIG_HTTPD_MAX_URI_HANDLERS=20
# Fixes static assertion: FLASH and PSRAM Mode configuration are not supported # Fixes static assertion: FLASH and PSRAM Mode configuration are not supported
CONFIG_IDF_EXPERIMENTAL_FEATURES=y CONFIG_IDF_EXPERIMENTAL_FEATURES=y
# Free up IRAM # Free up IRAM
@@ -11,8 +9,7 @@ CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y
CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y
# EmbedTLS # EmbedTLS
# Use TLS 1.2 because 1.3 conflicts with MbedTLS dynamic buffer CONFIG_MBEDTLS_SSL_PROTO_TLS1_3=y
CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y
# LVGL # LVGL
CONFIG_LV_USE_USER_DATA=y CONFIG_LV_USE_USER_DATA=y
CONFIG_LV_USE_FS_STDIO=y CONFIG_LV_USE_FS_STDIO=y
@@ -43,17 +40,3 @@ CONFIG_WL_SECTOR_MODE_SAFE=y
CONFIG_WL_SECTOR_MODE=1 CONFIG_WL_SECTOR_MODE=1
# Allow new i2c_master API (used by Tab5Keyboard for LP_I2C_NUM_0) to coexist with legacy i2c driver # Allow new i2c_master API (used by Tab5Keyboard for LP_I2C_NUM_0) to coexist with legacy i2c driver
CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK=y CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK=y
# Wi-Fi memory optimizations
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=3
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=5
CONFIG_ESP_WIFI_STATIC_TX_BUFFER=y
CONFIG_ESP_WIFI_TX_BUFFER_TYPE=0
CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM=5
CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=5
CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER=y
CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0
CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF=5
CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y
CONFIG_ESP_WIFI_TX_BA_WIN=5
CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y
CONFIG_ESP_WIFI_RX_BA_WIN=5
+2 -8
View File
@@ -54,8 +54,6 @@ if (DEFINED ENV{ESP_IDF_VERSION})
set(EXCLUDE_COMPONENTS "Simulator") set(EXCLUDE_COMPONENTS "Simulator")
idf_build_set_property(LINK_OPTIONS "-Wl,--allow-multiple-definition" APPEND)
# Panic handler wrapping is only available on Xtensa architecture # Panic handler wrapping is only available on Xtensa architecture
if (CONFIG_IDF_TARGET_ARCH_XTENSA) if (CONFIG_IDF_TARGET_ARCH_XTENSA)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND) idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND)
@@ -74,11 +72,8 @@ else ()
message("Building for sim target") message("Building for sim target")
add_compile_definitions(CONFIG_TT_DEVICE_ID="simulator") add_compile_definitions(CONFIG_TT_DEVICE_ID="simulator")
add_compile_definitions(CONFIG_TT_DEVICE_NAME="Simulator") add_compile_definitions(CONFIG_TT_DEVICE_NAME="Simulator")
add_compile_definitions(CONFIG_TT_DEVICE_VENDOR="")
add_compile_definitions(CONFIG_TT_DEVICE_NAME_SIMPLE="Simulator")
add_compile_definitions(CONFIG_TT_LAUNCHER_APP_ID="Launcher") add_compile_definitions(CONFIG_TT_LAUNCHER_APP_ID="Launcher")
add_compile_definitions(CONFIG_TT_AUTO_START_APP_ID="") add_compile_definitions(CONFIG_TT_AUTO_START_APP_ID="")
add_compile_definitions(CONFIG_TT_USER_DATA_LOCATION_INTERNAL)
endif () endif ()
project(Tactility) project(Tactility)
@@ -106,9 +101,8 @@ if (NOT DEFINED ENV{ESP_IDF_VERSION})
target_compile_definitions(freertos_kernel PUBLIC "projCOVERAGE_TEST=0") target_compile_definitions(freertos_kernel PUBLIC "projCOVERAGE_TEST=0")
# EmbedTLS # EmbedTLS
set(ENABLE_TESTING OFF CACHE BOOL "" FORCE) set(ENABLE_TESTING OFF)
set(ENABLE_PROGRAMS OFF CACHE BOOL "" FORCE) set(ENABLE_PROGRAMS OFF)
set(MBEDTLS_FATAL_WARNINGS OFF CACHE BOOL "" FORCE)
add_subdirectory(Libraries/mbedtls) add_subdirectory(Libraries/mbedtls)
# SDL # SDL
@@ -0,0 +1 @@
enableOnBoot=false
@@ -0,0 +1 @@
enableOnBoot=false
@@ -0,0 +1 @@
enableOnBoot=false
+5
View File
@@ -0,0 +1,5 @@
language=en-US
timeFormat24h=true
dateFormat=DD/MM/YYYY
region=EU
timezone=Europe/Amsterdam
@@ -485,7 +485,7 @@
<button class="btn btn-secondary btn-sm" onclick="goUp()" id="upBtn">Up</button> <button class="btn btn-secondary btn-sm" onclick="goUp()" id="upBtn">Up</button>
</div> </div>
<div class="toolbar-sep"></div> <div class="toolbar-sep"></div>
<input type="text" id="pathInput" value="" onkeydown="if(event.key==='Enter')refreshFiles()"> <input type="text" id="pathInput" value="/data" onkeydown="if(event.key==='Enter')refreshFiles()">
<button class="btn btn-primary btn-sm" onclick="refreshFiles()">Go</button> <button class="btn btn-primary btn-sm" onclick="refreshFiles()">Go</button>
<div class="toolbar-sep"></div> <div class="toolbar-sep"></div>
<div class="toolbar-group"> <div class="toolbar-group">
@@ -739,6 +739,7 @@ function renderDashboard(data) {
<div class="card"> <div class="card">
<h2>Quick Actions</h2> <h2>Quick Actions</h2>
<div class="actions"> <div class="actions">
<button class="btn btn-primary" onclick="syncAssets(this)">Sync Assets</button>
${data.features_enabled?.screenshot ? '<button class="btn btn-secondary" onclick="captureScreenshot(this)">Screenshot</button>' : ''} ${data.features_enabled?.screenshot ? '<button class="btn btn-secondary" onclick="captureScreenshot(this)">Screenshot</button>' : ''}
<button class="btn btn-danger" onclick="rebootDevice(this)">Reboot</button> <button class="btn btn-danger" onclick="rebootDevice(this)">Reboot</button>
</div> </div>
+63
View File
@@ -0,0 +1,63 @@
<!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>
+3
View File
@@ -0,0 +1,3 @@
{
"version": 0
}
@@ -13,7 +13,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
+21 -18
View File
@@ -1,22 +1,25 @@
general.vendor=BigTreeTech [general]
general.name=Panda Touch,K Touch vendor=BigTreeTech
name=Panda Touch,K Touch
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=OCT spiRam=true
hardware.spiRamSpeed=120M spiRamMode=OCT
hardware.esptoolFlashFreq=120M spiRamSpeed=120M
hardware.bluetooth=true esptoolFlashFreq=120M
hardware.usbHostEnabled=true bluetooth=true
usbHostEnabled=true
storage.userDataLocation=Internal [display]
size=5"
shape=rectangle
dpi=187
display.size=5" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=187 fontSize=18
lvgl.colorDepth=16
lvgl.fontSize=18
+15 -12
View File
@@ -1,16 +1,19 @@
general.vendor=CYD [general]
general.name=2432S024C vendor=CYD
name=2432S024C
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32 [hardware]
hardware.flashSize=4MB target=ESP32
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=SD [display]
size=2.4"
shape=rectangle
dpi=167
display.size=2.4" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=167
lvgl.colorDepth=16
+15 -12
View File
@@ -1,16 +1,19 @@
general.vendor=CYD [general]
general.name=2432S024R vendor=CYD
name=2432S024R
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32 [hardware]
hardware.flashSize=4MB target=ESP32
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=SD [display]
size=2.4"
shape=rectangle
dpi=167
display.size=2.4" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=167
lvgl.colorDepth=16
+17 -13
View File
@@ -1,18 +1,22 @@
general.vendor=CYD [general]
general.name=2432S028R vendor=CYD
name=2432S028R
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32 [hardware]
hardware.flashSize=4MB target=ESP32
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=SD [display]
size=2.8"
shape=rectangle
dpi=143
display.size=2.8" [cdn]
display.shape=rectangle warningMessage=There are 3 hardware variants of this board. This build works on the original variant only ("v1").
display.dpi=143
cdn.warningMessage=There are 3 hardware variants of this board. This build works on the original variant only ("v1"). [lvgl]
colorDepth=16
lvgl.colorDepth=16
+17 -13
View File
@@ -1,18 +1,22 @@
general.vendor=CYD [general]
general.name=2432S028R v3 vendor=CYD
name=2432S028R v3
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32 [hardware]
hardware.flashSize=4MB target=ESP32
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=SD [display]
size=2.8"
shape=rectangle
dpi=143
display.size=2.8" [cdn]
display.shape=rectangle warningMessage=There are 3 hardware variants of this board. This build only supports board version 3.
display.dpi=143
cdn.warningMessage=There are 3 hardware variants of this board. This build only supports board version 3. [lvgl]
colorDepth=16
lvgl.colorDepth=16
+15 -12
View File
@@ -1,16 +1,19 @@
general.vendor=CYD [general]
general.name=2432S032C vendor=CYD
name=2432S032C
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32 [hardware]
hardware.flashSize=4MB target=ESP32
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=SD [display]
size=3.2"
shape=rectangle
dpi=125
display.size=3.2" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=125
lvgl.colorDepth=16
+15 -12
View File
@@ -1,16 +1,19 @@
general.vendor=CYD [general]
general.name=3248S035C vendor=CYD
name=3248S035C
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32 [hardware]
hardware.flashSize=4MB target=ESP32
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=SD [display]
size=3.5"
shape=rectangle
dpi=165
display.size=3.5" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=165
lvgl.colorDepth=16
@@ -12,25 +12,9 @@
#include <esp_lcd_panel_ops.h> #include <esp_lcd_panel_ops.h>
#include <esp_lcd_panel_io_additions.h> #include <esp_lcd_panel_io_additions.h>
#include <esp_lcd_st7701.h> #include <esp_lcd_st7701.h>
#include <esp_rom_gpio.h>
#include <soc/spi_periph.h>
#include <driver/spi_master.h>
static const auto LOGGER = tt::Logger("St7701Display"); static const auto LOGGER = tt::Logger("St7701Display");
// GPIO47/48 are physically shared between this bit-banged 3-wire command bus
// and the SD card's real SPI2 bus (no alternate pins exist on this PCB).
// esp_lcd_new_panel_io_3wire_spi() reconfigures them as plain GPIO via
// gpio_config(), severing their SPI2 matrix routing. Reconnect them here once
// the vendor init sequence is done, so SD card reads/writes keep working.
// Safe only because nothing else calls into the ST7701 IO handle after boot.
static void reclaimSpiPinsForSdCard() {
esp_rom_gpio_connect_out_signal(GPIO_NUM_47, spi_periph_signal[SPI2_HOST].spid_out, false, false);
esp_rom_gpio_connect_out_signal(GPIO_NUM_48, spi_periph_signal[SPI2_HOST].spiclk_out, false, false);
gpio_set_direction(GPIO_NUM_47, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_NUM_48, GPIO_MODE_OUTPUT);
}
static const st7701_lcd_init_cmd_t st7701_lcd_init_cmds[] = { static const st7701_lcd_init_cmd_t st7701_lcd_init_cmds[] = {
// {cmd, { data }, data_size, delay_ms} // {cmd, { data }, data_size, delay_ms}
{0xFF, (uint8_t[]) {0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0}, {0xFF, (uint8_t[]) {0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
@@ -197,8 +181,6 @@ bool St7701Display::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lc
return false; return false;
} }
reclaimSpiPinsForSdCard();
return true; return true;
} }
+1 -1
View File
@@ -12,7 +12,7 @@ static error_t stop() {
return ERROR_NONE; return ERROR_NONE;
} }
Module cyd_4848s040c_module = { struct Module cyd_4848s040c_module = {
.name = "cyd-4848s040c", .name = "cyd-4848s040c",
.start = start, .start = start,
.stop = stop, .stop = stop,
+14 -1
View File
@@ -13,7 +13,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
@@ -28,4 +27,18 @@
pin-sda = <&gpio0 19 GPIO_FLAG_NONE>; pin-sda = <&gpio0 19 GPIO_FLAG_NONE>;
pin-scl = <&gpio0 45 GPIO_FLAG_NONE>; pin-scl = <&gpio0 45 GPIO_FLAG_NONE>;
}; };
spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
cs-gpios = <&gpio0 42 GPIO_FLAG_NONE>;
pin-mosi = <&gpio0 47 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 41 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 48 GPIO_FLAG_NONE>;
sdcard@0 {
compatible = "espressif,esp32-sdspi";
frequency-khz = <20000>;
};
};
}; };
+18 -15
View File
@@ -1,19 +1,22 @@
general.vendor=CYD [general]
general.name=4848S040C vendor=CYD
name=4848S040C
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=OCT spiRam=true
hardware.spiRamSpeed=80M spiRamMode=OCT
hardware.bluetooth=true spiRamSpeed=80M
bluetooth=true
storage.userDataLocation=SD [display]
size=4"
shape=rectangle
dpi=170
display.size=4" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=170
lvgl.colorDepth=16
-1
View File
@@ -14,7 +14,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
+25 -21
View File
@@ -1,26 +1,30 @@
general.vendor=CYD [general]
general.name=8048S043C vendor=CYD
general.incubating=false name=8048S043C
incubating=false
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=OCT spiRam=true
hardware.spiRamSpeed=80M spiRamMode=OCT
hardware.esptoolFlashFreq=80M spiRamSpeed=80M
hardware.bluetooth=true esptoolFlashFreq=80M
bluetooth=true
storage.userDataLocation=SD [display]
size=4.3"
shape=rectangle
dpi=217
display.size=4.3" [cdn]
display.shape=rectangle infoMessage=
display.dpi=217 warningMessage=
cdn.infoMessage= [lvgl]
cdn.warningMessage= theme=DefaultDark
colorDepth=16
lvgl.theme=DefaultDark fontSize=18
lvgl.colorDepth=16
lvgl.fontSize=18
+15 -12
View File
@@ -1,16 +1,19 @@
general.vendor=CYD [general]
general.name=E32R28T vendor=CYD
name=E32R28T
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32 [hardware]
hardware.flashSize=4MB target=ESP32
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=SD [display]
size=2.8"
shape=rectangle
dpi=143
display.size=2.8" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=143
lvgl.colorDepth=16
+15 -12
View File
@@ -1,16 +1,19 @@
general.vendor=CYD [general]
general.name=E32R32P vendor=CYD
name=E32R32P
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32 [hardware]
hardware.flashSize=4MB target=ESP32
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=SD [display]
size=2.8"
shape=rectangle
dpi=125
display.size=2.8" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=125
lvgl.colorDepth=16
@@ -1,21 +1,24 @@
general.vendor=Elecrow [general]
general.name=CrowPanel Advance 2.8" vendor=Elecrow
name=CrowPanel Advance 2.8"
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=OCT spiRam=true
hardware.spiRamSpeed=120M spiRamMode=OCT
hardware.tinyUsb=true spiRamSpeed=120M
hardware.esptoolFlashFreq=120M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=120M
bluetooth=true
storage.userDataLocation=SD [display]
size=2.8"
shape=rectangle
dpi=143
display.size=2.8" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=143
lvgl.colorDepth=16
@@ -15,7 +15,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
@@ -1,21 +1,24 @@
general.vendor=Elecrow [general]
general.name=CrowPanel Advance 3.5" vendor=Elecrow
name=CrowPanel Advance 3.5"
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=OCT spiRam=true
hardware.spiRamSpeed=120M spiRamMode=OCT
hardware.tinyUsb=true spiRamSpeed=120M
hardware.esptoolFlashFreq=120M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=120M
bluetooth=true
storage.userDataLocation=SD [display]
size=3.5"
shape=rectangle
dpi=165
display.size=3.5" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=165
lvgl.colorDepth=16
@@ -15,7 +15,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
@@ -1,22 +1,25 @@
general.vendor=Elecrow [general]
general.name=CrowPanel Advance 5.0" vendor=Elecrow
name=CrowPanel Advance 5.0"
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=OCT spiRam=true
hardware.spiRamSpeed=120M spiRamMode=OCT
hardware.tinyUsb=true spiRamSpeed=120M
hardware.esptoolFlashFreq=120M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=120M
bluetooth=true
storage.userDataLocation=SD [display]
size=5"
shape=rectangle
dpi=187
display.size=5" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=187 fontSize=18
lvgl.colorDepth=16
lvgl.fontSize=18
@@ -14,7 +14,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
@@ -1,16 +1,19 @@
general.vendor=Elecrow [general]
general.name=CrowPanel Basic 2.8" vendor=Elecrow
name=CrowPanel Basic 2.8"
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32 [hardware]
hardware.flashSize=4MB target=ESP32
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=SD [display]
size=2.8"
shape=rectangle
dpi=143
display.size=2.8" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=143
lvgl.colorDepth=16
@@ -1,16 +1,19 @@
general.vendor=Elecrow [general]
general.name=CrowPanel Basic 3.5" vendor=Elecrow
name=CrowPanel Basic 3.5"
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32 [hardware]
hardware.flashSize=4MB target=ESP32
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=SD [display]
size=3.5"
shape=rectangle
dpi=165
display.size=3.5" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=165
lvgl.colorDepth=16
@@ -1,22 +1,25 @@
general.vendor=Elecrow [general]
general.name=CrowPanel Basic 5.0" vendor=Elecrow
name=CrowPanel Basic 5.0"
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=4MB target=ESP32S3
hardware.spiRam=true flashSize=4MB
hardware.spiRamMode=OCT spiRam=true
hardware.spiRamSpeed=120M spiRamMode=OCT
hardware.tinyUsb=true spiRamSpeed=120M
hardware.esptoolFlashFreq=120M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=120M
bluetooth=true
storage.userDataLocation=SD [display]
size=5.0"
shape=rectangle
dpi=187
display.size=5.0" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=187 fontSize=18
lvgl.colorDepth=16
lvgl.fontSize=18
@@ -14,7 +14,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
-94
View File
@@ -1,94 +0,0 @@
#include "PwmBacklight.h"
#include "devices/Display.h"
#include "devices/Power.h"
#include <driver/gpio.h>
#include <tactility/device.h>
#include <tactility/drivers/i2c_controller.h>
#include <tactility/log.h>
#include <tactility/error.h>
#include <tactility/freertos/freertos.h>
#include <Tactility/hal/Configuration.h>
using namespace tt::hal;
static constexpr auto* TAG = "ES3C28P";
static constexpr uint8_t ES8311_I2C_ADDR = 0x18;
static error_t initCodec(::Device* i2c_controller) {
static constexpr uint8_t ENABLED_BULK_DATA[] = {
0x00, 0x80, // RESET/CSM POWER ON
0x01, 0x3F, // CLOCK_MANAGER/ use MCLK pin (external), all clocks on
0x02, 0x00, // CLOCK_MANAGER/ pre_div=1 pre_multi=1 (256x MCLK is correct ratio)
0x03, 0x10, // CLOCK_MANAGER/ fs_mode=0, adc_osr=16
0x04, 0x10, // CLOCK_MANAGER/ dac_osr=16
0x05, 0x00, // CLOCK_MANAGER/ adc_div=1, dac_div=1
0x06, 0x03, // CLOCK_MANAGER/ bclk_div=4
0x07, 0x00, // CLOCK_MANAGER/ lrck_h=0
0x08, 0xFF, // CLOCK_MANAGER/ lrck_l=255 (div=256)
0x09, 0x0C, // SDPIN_REG09/ DAC SDP format = standard I2S, word length = 16-bit
0x0A, 0x0C, // SDPOUT_REG0A/ ADC SDP format = standard I2S, word length = 16-bit
0x0D, 0x01, // SYSTEM/ Power up analog circuitry
0x0E, 0x02, // SYSTEM/ Enable analog PGA, enable ADC modulator
0x12, 0x00, // SYSTEM/ power-up DAC
0x13, 0x10, // SYSTEM/ Enable output to HP drive (headphone/speaker)
0x14, 0x1A, // SYSTEM/ Select Mic1p-Mic1n / max PGA gain (+30dB)
0x17, 0xFF, // ADC_REG17/ ADC Volume (MAXGAIN)
0x1C, 0x6A, // ADC_REG1C/ ADC Equalizer bypass, cancel DC offset in digital
0x32, 0xBF, // DAC_REG32/ DAC Volume (0xBF = 191)
0x37, 0x08, // DAC_REG37/ Bypass DAC equalizer
};
return i2c_controller_write_register_array(
i2c_controller,
ES8311_I2C_ADDR,
ENABLED_BULK_DATA,
sizeof(ENABLED_BULK_DATA),
pdMS_TO_TICKS(1000)
);
}
static bool initBoot() {
// 1. Initialize display backlight
if (!driver::pwmbacklight::init(LCD_PIN_BACKLIGHT)) {
return false;
}
// 2. Initialize and power on the FM8002E Audio Amplifier (GPIO 1, Active LOW)
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << GPIO_NUM_1);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&io_conf);
// Drive LOW to enable the speaker amplifier
gpio_set_level(GPIO_NUM_1, 0);
// 3. Configure the ES8311 Codec over the I2C bus
auto* i2c_bus = device_find_by_name("i2c0");
if (i2c_bus != nullptr) {
error_t error = initCodec(i2c_bus);
if (error != ERROR_NONE) {
LOG_E(TAG, "Failed to initialize ES8311 codec: %s", error_to_string(error));
}
} else {
LOG_E(TAG, "i2c0 bus not found, skipping ES8311 initialization");
}
return true;
}
static DeviceVector createDevices() {
return {
createDisplay(),
createPower()
};
}
extern const Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
};
@@ -1,49 +0,0 @@
#include "Display.h"
#include <Ft6x36Touch.h>
#include <Ili934xDisplay.h>
#include <PwmBacklight.h>
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
auto configuration = std::make_unique<Ft6x36Touch::Configuration>(
I2C_NUM_0,
240, // xMax (native portrait width)
320, // yMax (native portrait height)
true, // swapXy
true, // mirrorX
false, // mirrorY
GPIO_NUM_18, // Touch Reset (RST)
GPIO_NUM_17 // Touch Interrupt (INT)
);
return std::make_shared<Ft6x36Touch>(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 = true,
.mirrorX = false,
.mirrorY = false,
.invertColor = true,
.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);
}
-156
View File
@@ -1,156 +0,0 @@
#include "Power.h"
#include <Tactility/Logger.h>
#include <esp_adc/adc_oneshot.h>
#include <esp_adc/adc_cali.h>
#include <esp_adc/adc_cali_scheme.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
static const auto* TAG = "ES3C28P_Power";
// HW: BAT+ -> R14 200K + R15 200K -> GND => BAT_ADC middle, 2x divider, to GPIO9 ADC1_CH8
// Charger TP4054 CHRG pin only drives LED, not connected to ESP32 GPIO.
// So we infer charging by voltage behavior: rising trend and high voltage.
class Es3c28pPower final : public tt::hal::power::PowerDevice {
public:
Es3c28pPower() {
adc_oneshot_unit_init_cfg_t init_cfg = {
.unit_id = ADC_UNIT_1,
.clk_src = ADC_RTC_CLK_SRC_DEFAULT,
.ulp_mode = ADC_ULP_MODE_DISABLE,
};
if (adc_oneshot_new_unit(&init_cfg, &adc_handle) != ESP_OK) {
ESP_LOGE(TAG, "ADC unit init failed");
return;
}
adc_oneshot_chan_cfg_t chan_cfg = {
.atten = ADC_ATTEN_DB_12,
.bitwidth = ADC_BITWIDTH_12,
};
if (adc_oneshot_config_channel(adc_handle, ADC_CHANNEL_8, &chan_cfg) != ESP_OK) {
ESP_LOGE(TAG, "ADC channel config failed");
adc_oneshot_del_unit(adc_handle);
adc_handle = nullptr;
return;
}
adc_cali_curve_fitting_config_t cali_cfg = {
.unit_id = ADC_UNIT_1,
.atten = ADC_ATTEN_DB_12,
.bitwidth = ADC_BITWIDTH_12,
};
if (adc_cali_create_scheme_curve_fitting(&cali_cfg, &cali_handle) != ESP_OK) {
ESP_LOGW(TAG, "ADC cali scheme failed");
cali_handle = nullptr;
}
ESP_LOGI(TAG, "Battery ADC GPIO9 ADC1_CH8, 2x divider, cali %s", cali_handle ? "OK" : "none");
}
~Es3c28pPower() override {
if (cali_handle) adc_cali_delete_scheme_curve_fitting(cali_handle);
if (adc_handle) adc_oneshot_del_unit(adc_handle);
}
std::string getName() const override { return "ES3C28P Power"; }
std::string getDescription() const override { return "Battery GPIO9 ADC1_CH8 + TP4054 inferred charging"; }
bool supportsMetric(MetricType type) const override {
switch (type) {
case MetricType::BatteryVoltage:
case MetricType::ChargeLevel:
case MetricType::IsCharging:
return adc_handle != nullptr;
default: return false;
}
}
bool getMetric(MetricType type, MetricData& data) override {
if (!adc_handle) return false;
int raw = 0;
int mv = 0;
if (adc_oneshot_read(adc_handle, ADC_CHANNEL_8, &raw) != ESP_OK) return false;
if (cali_handle) {
if (adc_cali_raw_to_voltage(cali_handle, raw, &mv) != ESP_OK) return false;
} else {
mv = (raw * 3300) / 4095;
}
int vbat_mv = mv * 2; // 200K/200K divider
// Track voltage trend for charging detection
const int now_ms = xTaskGetTickCount() * portTICK_PERIOD_MS;
if (last_read_ms == 0) {
last_vbat_mv = vbat_mv;
last_read_ms = now_ms;
}
// Update trend every 2 seconds to avoid noise
if (now_ms - last_read_ms > 2000) {
// If voltage increased by at least 10mV in 2s, likely charging
if (vbat_mv > last_vbat_mv + 10) {
rising_count++;
falling_count = 0;
} else if (vbat_mv < last_vbat_mv - 10) {
falling_count++;
rising_count = 0;
if (falling_count > 3) is_charging_trend = false;
}
last_vbat_mv = vbat_mv;
last_read_ms = now_ms;
}
switch (type) {
case MetricType::BatteryVoltage:
data.valueAsUint32 = (uint32_t)vbat_mv;
return true;
case MetricType::ChargeLevel: {
uint8_t pct;
if (vbat_mv <= 2500) pct = 0;
else if (vbat_mv >= 4200) pct = 100;
else pct = (uint8_t)((vbat_mv - 2500) / 17);
data.valueAsUint8 = pct;
return true;
}
case MetricType::IsCharging: {
// Cases:
// 1. No battery / wall powered: vbat <500mV or >5000mV (VBUS via MOSFET) => charging true
if (vbat_mv < 500 || vbat_mv > 5000) {
data.valueAsBool = true;
is_charging_trend = true;
return true;
}
// 2. High voltage near full with charger attached: >=4150mV => charging (charger holds at 4.2V)
if (vbat_mv >= 4150) {
data.valueAsBool = true;
is_charging_trend = true;
return true;
}
// 3. Rising trend indicates charging
if (rising_count >= 2) {
is_charging_trend = true;
}
// 4. If previously charging and voltage still >=4000, keep charging until drop
if (is_charging_trend && vbat_mv >= 4000) {
data.valueAsBool = true;
return true;
}
data.valueAsBool = is_charging_trend;
return true;
}
default: return false;
}
}
private:
adc_oneshot_unit_handle_t adc_handle = nullptr;
adc_cali_handle_t cali_handle = nullptr;
int last_vbat_mv = 0;
int last_read_ms = 0;
int rising_count = 0;
int falling_count = 0;
bool is_charging_trend = false;
};
std::shared_ptr<tt::hal::power::PowerDevice> createPower() {
return std::make_shared<Es3c28pPower>();
}
-5
View File
@@ -1,5 +0,0 @@
#pragma once
#include <Tactility/hal/power/PowerDevice.h>
#include <memory>
std::shared_ptr<tt::hal::power::PowerDevice> createPower();
-25
View File
@@ -1,25 +0,0 @@
general.vendor=LCDWIKI
general.name=ES3C28P
apps.launcherAppId=Launcher
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=120M
hardware.tinyUsb=true
hardware.usbHostEnabled=false
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
display.size=2.8"
display.shape=rectangle
display.dpi=143
lvgl.colorDepth=16
storage.userDataLocation=SD
sdkconfig.CONFIG_LV_CACHE_DEF_SIZE=524288
sdkconfig.CONFIG_LV_IMAGE_HEADER_CACHE_DEF_CNT=16
-3
View File
@@ -1,3 +0,0 @@
dependencies:
- Platforms/platform-esp32
dts: es3c28p.dts
-81
View File
@@ -1,81 +0,0 @@
/dts-v1/;
#include <tactility/bindings/root.h>
#include <tactility/bindings/esp32_ble.h>
#include <tactility/bindings/esp32_gpio.h>
#include <tactility/bindings/esp32_i2c.h>
#include <tactility/bindings/esp32_sdmmc.h>
#include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/esp32_i2s.h>
#include <tactility/bindings/esp32_usbhost.h>
/ {
compatible = "root";
model = "LCDWIKI ES3C28P";
ble0 {
compatible = "espressif,esp32-ble";
};
gpio0 {
compatible = "espressif,esp32-gpio";
gpio-count = <49>;
};
i2c0 {
compatible = "espressif,esp32-i2c";
port = <I2C_NUM_0>;
clock-frequency = <100000>;
pin-sda = <&gpio0 16 GPIO_FLAG_NONE>;
pin-scl = <&gpio0 15 GPIO_FLAG_NONE>;
};
i2s0 {
compatible = "espressif,esp32-i2s";
port = <I2S_NUM_0>;
pin-bclk = <&gpio0 5 GPIO_FLAG_NONE>;
pin-ws = <&gpio0 7 GPIO_FLAG_NONE>;
pin-data-out = <&gpio0 8 GPIO_FLAG_NONE>;
pin-data-in = <&gpio0 6 GPIO_FLAG_NONE>;
pin-mclk = <&gpio0 4 GPIO_FLAG_NONE>;
};
display_spi: spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
pin-mosi = <&gpio0 11 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 12 GPIO_FLAG_NONE>;
};
sdmmc0 {
compatible = "espressif,esp32-sdmmc";
pin-clk = <&gpio0 38 GPIO_FLAG_NONE>;
pin-cmd = <&gpio0 40 GPIO_FLAG_NONE>;
pin-d0 = <&gpio0 39 GPIO_FLAG_NONE>;
pin-d1 = <&gpio0 41 GPIO_FLAG_NONE>;
pin-d2 = <&gpio0 48 GPIO_FLAG_NONE>;
pin-d3 = <&gpio0 47 GPIO_FLAG_NONE>;
slot = <SDMMC_HOST_SLOT_1>;
bus-width = <4>;
};
usbhost0 {
compatible = "espressif,esp32-usbhost";
status = "disabled";
usbhosthid0 {
compatible = "espressif,esp32-usbhost-hid";
status = "disabled";
};
usbhostmidi0 {
compatible = "espressif,esp32-usbhost-midi";
status = "disabled";
};
usbhostmsc0 {
compatible = "espressif,esp32-usbhost-msc";
status = "disabled";
};
};
};
+9 -7
View File
@@ -1,10 +1,12 @@
general.vendor=Generic [general]
general.name=ESP32 vendor=Generic
name=ESP32
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32 [hardware]
hardware.flashSize=8MB target=ESP32
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=Internal
+9 -7
View File
@@ -1,10 +1,12 @@
general.vendor=Generic [general]
general.name=ESP32-C6 vendor=Generic
name=ESP32-C6
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32C6 [hardware]
hardware.flashSize=8MB target=ESP32C6
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=Internal
+9 -7
View File
@@ -1,10 +1,12 @@
general.vendor=Generic [general]
general.name=ESP32-P4 vendor=Generic
name=ESP32-P4
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32P4 [hardware]
hardware.flashSize=8MB target=ESP32P4
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=Internal
+9 -7
View File
@@ -1,10 +1,12 @@
general.vendor=Generic [general]
general.name=ESP32-S3 vendor=Generic
name=ESP32-S3
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=8MB target=ESP32S3
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=Internal
@@ -1,29 +1,33 @@
general.vendor=Guition [general]
general.name=JC1060P470C-I-W-Y vendor=Guition
name=JC1060P470C-I-W-Y
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32P4 [hardware]
hardware.flashSize=16MB target=ESP32P4
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=OCT spiRam=true
hardware.spiRamSpeed=200M spiRamMode=OCT
hardware.esptoolFlashFreq=80M spiRamSpeed=200M
hardware.bluetooth=true esptoolFlashFreq=80M
bluetooth=true
storage.userDataLocation=SD [display]
size=7"
shape=rectangle
dpi=187
display.size=7" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=187
lvgl.colorDepth=16 [sdkconfig]
CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
sdkconfig.CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16 CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30
sdkconfig.CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
sdkconfig.CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y CONFIG_ESP_HOSTED_ENABLED=y
sdkconfig.CONFIG_ESP_HOSTED_ENABLED=y CONFIG_ESP_HOSTED_P4_DEV_BOARD_FUNC_BOARD=y
sdkconfig.CONFIG_ESP_HOSTED_P4_DEV_BOARD_FUNC_BOARD=y CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE=y
sdkconfig.CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE=y CONFIG_SLAVE_IDF_TARGET_ESP32C6=y
sdkconfig.CONFIG_SLAVE_IDF_TARGET_ESP32C6=y CONFIG_ESP_HOSTED_USE_MEMPOOL=n
sdkconfig.CONFIG_ESP_HOSTED_USE_MEMPOOL=n
@@ -21,7 +21,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
+15 -12
View File
@@ -1,16 +1,19 @@
general.vendor=Guition [general]
general.name=JC2432W328C vendor=Guition
name=JC2432W328C
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32 [hardware]
hardware.flashSize=4MB target=ESP32
hardware.spiRam=false flashSize=4MB
spiRam=false
storage.userDataLocation=SD [display]
size=2.8"
shape=rectangle
dpi=143
display.size=2.8" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=143
lvgl.colorDepth=16
+20 -17
View File
@@ -1,21 +1,24 @@
general.vendor=Guition [general]
general.name=JC3248W535C vendor=Guition
name=JC3248W535C
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=OCT spiRam=true
hardware.spiRamSpeed=120M spiRamMode=OCT
hardware.tinyUsb=true spiRamSpeed=120M
hardware.esptoolFlashFreq=120M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=120M
bluetooth=true
storage.userDataLocation=SD [display]
size=3.5"
shape=rectangle
dpi=165
display.size=3.5" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=165
lvgl.colorDepth=16
@@ -16,7 +16,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
+20 -17
View File
@@ -1,21 +1,24 @@
general.vendor=Guition [general]
general.name=JC8048W550C vendor=Guition
name=JC8048W550C
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=OCT spiRam=true
hardware.spiRamSpeed=80M spiRamMode=OCT
hardware.esptoolFlashFreq=80M spiRamSpeed=80M
hardware.bluetooth=true esptoolFlashFreq=80M
bluetooth=true
storage.userDataLocation=SD [display]
size=5"
shape=rectangle
dpi=187
display.size=5" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=187 fontSize=18
lvgl.colorDepth=16
lvgl.fontSize=18
@@ -15,7 +15,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
@@ -1,26 +1,30 @@
general.vendor=Heltec [general]
general.name=WiFi LoRa 32 v3 vendor=Heltec
general.incubating=true name=WiFi LoRa 32 v3
incubating=true
apps.launcherAppId=Launcher [apps]
apps.autoStartAppId=ApWebServer launcherAppId=Launcher
autoStartAppId=ApWebServer
hardware.target=ESP32S3 [hardware]
hardware.flashSize=8MB target=ESP32S3
hardware.spiRam=false flashSize=8MB
hardware.tinyUsb=true spiRam=false
hardware.esptoolFlashFreq=120M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=120M
bluetooth=true
storage.userDataLocation=Internal [display]
size=0.96"
shape=rectangle
dpi=149
display.size=0.96" [cdn]
display.shape=rectangle infoMessage=Due to the small size of the screen, the icons don't render properly.
display.dpi=149
cdn.infoMessage=Due to the small size of the screen, the icons don't render properly. [lvgl]
theme=Mono
lvgl.theme=Mono colorDepth=16
lvgl.colorDepth=16 uiScale=70
lvgl.uiScale=70 uiDensity=compact
lvgl.uiDensity=compact
@@ -11,7 +11,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
@@ -38,7 +38,7 @@ std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty, .backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
.resetPin = GPIO_NUM_NC, .resetPin = GPIO_NUM_NC,
.lvglSwapBytes = false, .lvglSwapBytes = false,
.buffSpiram = false // Enabling leads to crashes when refreshing App Hub list .buffSpiram = true
}; };
auto spi_configuration = std::make_shared<St7789Display::SpiConfiguration>(St7789Display::SpiConfiguration { auto spi_configuration = std::make_shared<St7789Display::SpiConfiguration>(St7789Display::SpiConfiguration {
@@ -9,7 +9,7 @@ constexpr auto LCD_PIN_CS = GPIO_NUM_12;
constexpr auto LCD_PIN_DC = GPIO_NUM_11; // RS constexpr auto LCD_PIN_DC = GPIO_NUM_11; // RS
constexpr auto LCD_HORIZONTAL_RESOLUTION = 320; constexpr auto LCD_HORIZONTAL_RESOLUTION = 320;
constexpr auto LCD_VERTICAL_RESOLUTION = 240; constexpr auto LCD_VERTICAL_RESOLUTION = 240;
constexpr auto LCD_BUFFER_HEIGHT = (LCD_VERTICAL_RESOLUTION / 10); constexpr auto LCD_BUFFER_HEIGHT = (LCD_VERTICAL_RESOLUTION / 3);
constexpr auto LCD_BUFFER_SIZE = (LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT); constexpr auto LCD_BUFFER_SIZE = (LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT);
constexpr auto LCD_SPI_TRANSFER_SIZE_LIMIT = LCD_BUFFER_SIZE * LV_COLOR_DEPTH / 8; constexpr auto LCD_SPI_TRANSFER_SIZE_LIMIT = LCD_BUFFER_SIZE * LV_COLOR_DEPTH / 8;
+22 -18
View File
@@ -1,23 +1,27 @@
general.vendor=LilyGO [general]
general.name=T-Deck,T-Deck Plus vendor=LilyGO
name=T-Deck,T-Deck Plus
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=OCT spiRam=true
hardware.spiRamSpeed=120M spiRamMode=OCT
hardware.tinyUsb=true spiRamSpeed=120M
hardware.esptoolFlashFreq=120M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=120M
bluetooth=true
storage.userDataLocation=SD [display]
size=2.8"
shape=rectangle
dpi=143
display.size=2.8" [cdn]
display.shape=rectangle infoMessage=To put the device into bootloader mode: <br/>1. Press the trackball and then the reset button at the same time,<br/>2. Let go of the reset button, then the trackball.<br/><br/>When this website reports that flashing is finished, you likely have to press the reset button.
display.dpi=143
cdn.infoMessage=To put the device into bootloader mode: <br/>1. Press the trackball and then the reset button at the same time,<br/>2. Let go of the reset button, then the trackball.<br/><br/>When this website reports that flashing is finished, you likely have to press the reset button. [lvgl]
colorDepth=16
lvgl.colorDepth=16
-1
View File
@@ -18,7 +18,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
+21 -18
View File
@@ -1,22 +1,25 @@
general.vendor=LilyGO [general]
general.name=T-Display S3 vendor=LilyGO
name=T-Display S3
apps.launcherAppId=Launcher [apps]
apps.autoStartAppId=ApWebServer launcherAppId=Launcher
autoStartAppId=ApWebServer
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=OCT spiRam=true
hardware.spiRamSpeed=120M spiRamMode=OCT
hardware.tinyUsb=true spiRamSpeed=120M
hardware.esptoolFlashFreq=120M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=120M
bluetooth=true
storage.userDataLocation=Internal [display]
size=1.9"
shape=rectangle
dpi=191
display.size=1.9" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=191
lvgl.colorDepth=16
@@ -10,7 +10,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
@@ -12,7 +12,7 @@ constexpr auto LCD_PIN_RESET = GPIO_NUM_23;
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_4; constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_4;
constexpr auto LCD_HORIZONTAL_RESOLUTION = 135; constexpr auto LCD_HORIZONTAL_RESOLUTION = 135;
constexpr auto LCD_VERTICAL_RESOLUTION = 240; constexpr auto LCD_VERTICAL_RESOLUTION = 240;
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 6; constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 3;
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT; constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay(); std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
+19 -16
View File
@@ -1,20 +1,23 @@
general.vendor=LilyGO [general]
general.name=T-Display vendor=LilyGO
general.incubating=true name=T-Display
incubating=true
apps.launcherAppId=Launcher [apps]
apps.autoStartAppId=ApWebServer launcherAppId=Launcher
autoStartAppId=ApWebServer
hardware.target=ESP32 [hardware]
hardware.flashSize=16MB target=ESP32
hardware.spiRam=false flashSize=16MB
hardware.esptoolFlashFreq=80M spiRam=false
esptoolFlashFreq=80M
storage.userDataLocation=Internal [display]
size=1.14"
shape=rectangle
dpi=242
display.size=1.14" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=242 uiDensity=compact
lvgl.colorDepth=16
lvgl.uiDensity=compact
+22 -19
View File
@@ -1,23 +1,26 @@
general.vendor=LilyGO [general]
general.name=T-Dongle S3 vendor=LilyGO
general.incubating=true name=T-Dongle S3
incubating=true
apps.launcherAppId=Launcher [apps]
apps.autoStartAppId=ApWebServer launcherAppId=Launcher
autoStartAppId=ApWebServer
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.spiRam=false flashSize=16MB
hardware.tinyUsb=true spiRam=false
hardware.esptoolFlashFreq=120M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=120M
bluetooth=true
storage.userDataLocation=SD [display]
size=0.96"
shape=rectangle
dpi=186
display.size=0.96" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=186 uiDensity=compact
fontSize=10
lvgl.colorDepth=16
lvgl.uiDensity=compact
lvgl.fontSize=10
@@ -15,7 +15,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
+20 -17
View File
@@ -1,21 +1,24 @@
general.vendor=LilyGO [general]
general.name=T-HMI vendor=LilyGO
name=T-HMI
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=OCT spiRam=true
hardware.spiRamSpeed=120M spiRamMode=OCT
hardware.tinyUsb=true spiRamSpeed=120M
hardware.esptoolFlashFreq=120M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=120M
bluetooth=true
storage.userDataLocation=SD [display]
size=2.8"
shape=rectangle
dpi=125
display.size=2.8" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=125
lvgl.colorDepth=16
-1
View File
@@ -15,7 +15,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
+22 -19
View File
@@ -1,23 +1,26 @@
general.vendor=LilyGO [general]
general.name=T-Lora Pager vendor=LilyGO
name=T-Lora Pager
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.flashMode=DIO flashSize=16MB
hardware.spiRam=true flashMode=DIO
hardware.spiRamMode=AUTO spiRam=true
hardware.spiRamSpeed=120M spiRamMode=AUTO
hardware.tinyUsb=true spiRamSpeed=120M
hardware.esptoolFlashFreq=40M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=40M
bluetooth=true
storage.userDataLocation=SD [display]
size=2.33"
shape=rectangle
dpi=227
display.size=2.33" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=227 dpi=150
lvgl.colorDepth=16
lvgl.dpi=150
@@ -17,7 +17,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
+19 -16
View File
@@ -1,21 +1,24 @@
general.vendor=M5Stack [general]
general.name=Cardputer Adv vendor=M5Stack
name=Cardputer Adv
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=8MB target=ESP32S3
hardware.spiRam=false flashSize=8MB
hardware.tinyUsb=true spiRam=false
hardware.esptoolFlashFreq=120M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=120M
bluetooth=true
storage.userDataLocation=SD [display]
size=1.14"
display.size=1.14" shape=rectangle
display.shape=rectangle
# TODO: dps is actually 242, but this breaks UI (button selection becomes invisible and switch visibility is reduced) # TODO: dps is actually 242, but this breaks UI (button selection becomes invisible and switch visibility is reduced)
display.dpi=139 dpi=139
lvgl.colorDepth=16 [lvgl]
lvgl.uiDensity=compact colorDepth=16
uiDensity=compact
@@ -19,7 +19,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
+19 -16
View File
@@ -1,21 +1,24 @@
general.vendor=M5Stack [general]
general.name=Cardputer,Cardputer v1.1 vendor=M5Stack
name=Cardputer,Cardputer v1.1
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=8MB target=ESP32S3
hardware.spiRam=false flashSize=8MB
hardware.tinyUsb=true spiRam=false
hardware.esptoolFlashFreq=120M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=120M
bluetooth=true
storage.userDataLocation=SD [display]
size=1.14"
display.size=1.14" shape=rectangle
display.shape=rectangle
# TODO: dps is actually 242, but this breaks UI (button selection becomes invisible and switch visibility is reduced) # TODO: dps is actually 242, but this breaks UI (button selection becomes invisible and switch visibility is reduced)
display.dpi=139 dpi=139
lvgl.colorDepth=16 [lvgl]
lvgl.uiDensity=compact colorDepth=16
uiDensity=compact
@@ -18,7 +18,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
+20 -16
View File
@@ -1,21 +1,25 @@
general.vendor=M5Stack [general]
general.name=Core2 vendor=M5Stack
name=Core2
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32 [hardware]
hardware.flashSize=16MB target=ESP32
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=QUAD spiRam=true
hardware.spiRamSpeed=80M spiRamMode=QUAD
hardware.esptoolFlashFreq=80M spiRamSpeed=80M
esptoolFlashFreq=80M
storage.userDataLocation=SD [display]
size=2"
shape=rectangle
dpi=200
display.size=2" [cdn]
display.shape=rectangle warningMessage=This board implementation concerns the original Core2 hardware and **not** the v1.1 variant
display.dpi=200
cdn.warningMessage=This board implementation concerns the original Core2 hardware and **not** the v1.1 variant [lvgl]
colorDepth=16
lvgl.colorDepth=16
+20 -17
View File
@@ -1,21 +1,24 @@
general.vendor=M5Stack [general]
general.name=CoreS3 vendor=M5Stack
name=CoreS3
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=QUAD spiRam=true
hardware.spiRamSpeed=120M spiRamMode=QUAD
hardware.tinyUsb=true spiRamSpeed=120M
hardware.esptoolFlashFreq=120M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=120M
bluetooth=true
storage.userDataLocation=SD [display]
size=2"
shape=rectangle
dpi=200
display.size=2" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=200
lvgl.colorDepth=16
@@ -20,7 +20,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
+26 -20
View File
@@ -1,26 +1,32 @@
general.vendor=M5Stack [general]
general.name=PaperS3 vendor=M5Stack
general.incubating=true name=PaperS3
incubating=true
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=esp32s3 [hardware]
hardware.flashSize=16MB target=esp32s3
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=OPI spiRam=true
hardware.spiRamSpeed=80M spiRamMode=OPI
hardware.esptoolFlashFreq=80M spiRamSpeed=80M
hardware.tinyUsb=true esptoolFlashFreq=80M
hardware.bluetooth=true tinyUsb=true
bluetooth=true
storage.userDataLocation=SD [display]
size=4.7"
shape=rectangle
dpi=235
display.size=4.7" [lvgl]
display.shape=rectangle colorDepth=8
display.dpi=235 fontSize=24
theme=Mono
[sdkconfig]
CONFIG_EPD_DISPLAY_TYPE_ED047TC2=y
lvgl.colorDepth=8
lvgl.fontSize=24
lvgl.theme=Mono
sdkconfig.CONFIG_EPD_DISPLAY_TYPE_ED047TC2=y
@@ -15,7 +15,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
+20 -17
View File
@@ -1,21 +1,24 @@
general.vendor=M5Stack [general]
general.name=StackChan vendor=M5Stack
name=StackChan
apps.launcherAppId=Launcher [apps]
launcherAppId=Launcher
hardware.target=ESP32S3 [hardware]
hardware.flashSize=16MB target=ESP32S3
hardware.spiRam=true flashSize=16MB
hardware.spiRamMode=QUAD spiRam=true
hardware.spiRamSpeed=120M spiRamMode=QUAD
hardware.tinyUsb=true spiRamSpeed=120M
hardware.esptoolFlashFreq=120M tinyUsb=true
hardware.bluetooth=true esptoolFlashFreq=120M
bluetooth=true
storage.userDataLocation=SD [display]
size=2"
shape=rectangle
dpi=200
display.size=2" [lvgl]
display.shape=rectangle colorDepth=16
display.dpi=200
lvgl.colorDepth=16
@@ -22,7 +22,6 @@
ble0 { ble0 {
compatible = "espressif,esp32-ble"; compatible = "espressif,esp32-ble";
status = "disabled";
}; };
gpio0 { gpio0 {
@@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
idf_component_register( idf_component_register(
SRCS ${SOURCE_FILES} SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source" INCLUDE_DIRS "Source"
REQUIRES Tactility esp_lvgl_port ILI934x FT6x36 PwmBacklight driver vfs fatfs esp_adc REQUIRES Tactility esp_lvgl_port esp_lcd AXP192 ST7789 ButtonControl
) )
@@ -0,0 +1,29 @@
#include "devices/Display.h"
#include "devices/Power.h"
#include <Tactility/hal/Configuration.h>
#include <ButtonControl.h>
using namespace tt::hal;
static bool initBoot() {
// CH552 applies 4 V to GPIO 0, which reduces Wi-Fi sensitivity
// Setting output to high adds a bias of 3.3 V and suppresses over-voltage:
gpio::configure(0, gpio::Mode::Output, false, false);
gpio::setLevel(0, true);
return initAxp();
}
static DeviceVector createDevices() {
return {
getAxp192(),
ButtonControl::createTwoButtonControl(37, 39),
createDisplay()
};
}
extern const Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
};
@@ -0,0 +1,67 @@
#include "Display.h"
#include "Power.h"
#include <Tactility/Logger.h>
#include <St7789Display.h>
#include <bitset>
static const auto LOGGER = tt::Logger("StickCPlus");
static void setBacklightOn(bool on) {
const auto axp = getAxp192();
const auto* driver = axp->getAxp192();
uint8_t state;
if (axp192_read(driver, AXP192_DCDC13_LDO23_CONTROL, &state) != AXP192_OK) {
LOGGER.info("Failed to read LCD brightness state");
return;
}
std::bitset<8> new_state = state;
if (new_state[2] != on) {
new_state[2] = on;
const auto new_state_long = new_state.to_ulong();
axp192_write(driver, AXP192_DCDC13_LDO23_CONTROL, static_cast<uint8_t>(new_state_long)); // Display on/off
}
}
static void setBrightness(uint8_t brightness) {
const auto axp = getAxp192();
if (brightness)
{
brightness = (((brightness >> 1) + 8) / 13) + 5;
setBacklightOn(true);
axp192_write(axp->getAxp192(), AXP192_LDO23_VOLTAGE, brightness << 4); // Display brightness
}
else
{
setBacklightOn(false);
}
}
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
St7789Display::Configuration panel_configuration = {
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
.verticalResolution = LCD_VERTICAL_RESOLUTION,
.gapX = 52,
.gapY = 40,
.swapXY = false,
.mirrorX = false,
.mirrorY = false,
.invertColor = true,
.bufferSize = LCD_BUFFER_SIZE,
.touch = nullptr,
.backlightDutyFunction = setBrightness,
.resetPin = LCD_PIN_RESET,
.lvglSwapBytes = false
};
auto spi_configuration = std::make_shared<St7789Display::SpiConfiguration>(St7789Display::SpiConfiguration {
.spiHostDevice = LCD_SPI_HOST,
.csPin = LCD_PIN_CS,
.dcPin = LCD_PIN_DC,
.pixelClockFrequency = 40'000'000,
.transactionQueueDepth = 10
});
return std::make_shared<St7789Display>(panel_configuration, spi_configuration);
}
@@ -1,20 +1,18 @@
#pragma once #pragma once
#include <Tactility/hal/display/DisplayDevice.h> #include <Tactility/hal/display/DisplayDevice.h>
#include <memory>
#include <driver/gpio.h> #include <driver/gpio.h>
#include <driver/spi_common.h> #include <driver/spi_common.h>
#include <memory>
// Display
constexpr auto LCD_SPI_HOST = SPI2_HOST; constexpr auto LCD_SPI_HOST = SPI2_HOST;
constexpr auto LCD_PIN_CS = GPIO_NUM_10; constexpr auto LCD_PIN_CS = GPIO_NUM_5;
constexpr auto LCD_PIN_DC = GPIO_NUM_46; constexpr auto LCD_PIN_DC = GPIO_NUM_23;
constexpr auto LCD_HORIZONTAL_RESOLUTION = 320; constexpr auto LCD_PIN_RESET = GPIO_NUM_18;
constexpr auto LCD_HORIZONTAL_RESOLUTION = 135;
constexpr auto LCD_VERTICAL_RESOLUTION = 240; constexpr auto LCD_VERTICAL_RESOLUTION = 240;
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 10; constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 3;
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT; constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
constexpr auto LCD_SPI_TRANSFER_SIZE_LIMIT = LCD_BUFFER_SIZE * LV_COLOR_DEPTH / 8;
// Backlight pin
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_45;
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay(); std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
@@ -0,0 +1,39 @@
#include <Axp192.h>
#include <tactility/device.h>
static std::shared_ptr<Axp192> axp192 = nullptr;
std::shared_ptr<Axp192> createAxp192() {
assert(axp192 == nullptr);
auto configuration = std::make_unique<Axp192::Configuration>(device_find_by_name("i2c_internal"));
axp192 = std::make_shared<Axp192>(std::move(configuration));
return axp192;
}
std::shared_ptr<Axp192> getAxp192() {
assert(axp192 != nullptr);
return axp192;
}
bool initAxp() {
const auto axp = createAxp192();
return axp->init([](auto* driver) {
// Reference: https://github.com/pr3y/Bruce/blob/main/lib/utility/AXP192.cpp
axp192_ioctl(driver, AXP192_LDO23_VOLTAGE, 3300); // LCD backlight
axp192_ioctl(driver, AXP192_ADC_ENABLE_1, 0xff); // Set all ADC enabled
axp192_ioctl(driver, AXP192_CHARGE_CONTROL_1, 0xc0); // Battery charging at 4.2 V and 100 mA
axp192_ioctl(driver, AXP192_ADC_ENABLE_1, 0xff); // Enable battery, AC in, Vbus, APS ADC
uint8_t buffer;
axp192_read(driver, AXP192_DCDC13_LDO23_CONTROL, &buffer);
axp192_ioctl(driver, AXP192_DCDC13_LDO23_CONTROL, buffer | 0x4D); // Enable Ext, LDO2, LDO3, DCDC1
axp192_ioctl(driver, AXP192_PEK, 0x0C); // 128 ms power on, 4 s power off
axp192_ioctl(driver, AXP192_GPIO0_LDOIO0_VOLTAGE, 0xF0); // 3.3 V RTC voltage
axp192_ioctl(driver, AXP192_GPIO0_CONTROL, 0x02); // Set GPIO0 to LDO
axp192_ioctl(driver, AXP192_VBUS_IPSOUT_CHANNEL, 0x80); // Disable Vbus hold limit
axp192_ioctl(driver, AXP192_BATTERY_CHARGE_HIGH_TEMP, 0xFC); // Set temperature protection
axp192_ioctl(driver, AXP192_BATTERY_CHARGE_CONTROL, 0xA2); // Enable RTC BAT charge
axp192_ioctl(driver, AXP192_SHUTDOWN_BATTERY_CHGLED_CONTROL, 0x46); // Enable bat detection
return true;
});
}
@@ -0,0 +1,9 @@
#pragma once
#include <Axp192.h>
bool initAxp();
// Must call initAxp() first before this returns a non-nullptr response
std::shared_ptr<Axp192> getAxp192();
@@ -12,8 +12,8 @@ static error_t stop() {
return ERROR_NONE; return ERROR_NONE;
} }
struct Module es3c28p_module = { struct Module m5stack_stickc_plus_module = {
.name = "es3c28p", .name = "m5stack-stickc-plus",
.start = start, .start = start,
.stop = stop, .stop = stop,
.symbols = nullptr, .symbols = nullptr,
@@ -0,0 +1,23 @@
[general]
vendor=M5Stack
name=StickC Plus
incubating=true
[apps]
launcherAppId=Launcher
autoStartAppId=ApWebServer
[hardware]
target=ESP32
flashSize=4MB
spiRam=false
esptoolFlashFreq=80M
[display]
size=1.14"
shape=rectangle
dpi=242
[lvgl]
colorDepth=16
uiDensity=compact
@@ -0,0 +1,5 @@
dependencies:
- Platforms/platform-esp32
- Drivers/mpu6886-module
- Drivers/bm8563-module
dts: m5stack,stickc-plus.dts
@@ -0,0 +1,66 @@
/dts-v1/;
#include <tactility/bindings/root.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 <bindings/mpu6886.h>
#include <bindings/bm8563.h>
#include <tactility/bindings/display_placeholder.h>
/ {
compatible = "root";
model = "M5Stack StickC Plus";
gpio0 {
compatible = "espressif,esp32-gpio";
gpio-count = <40>;
};
i2c_internal {
compatible = "espressif,esp32-i2c";
port = <I2C_NUM_0>;
clock-frequency = <400000>;
pin-sda = <&gpio0 21 GPIO_FLAG_NONE>;
pin-scl = <&gpio0 22 GPIO_FLAG_NONE>;
mpu6886 {
compatible = "invensense,mpu6886";
reg = <0x68>;
};
bm8563 {
compatible = "belling,bm8563";
reg = <0x51>;
};
};
i2c_grove {
compatible = "espressif,esp32-i2c";
port = <I2C_NUM_1>;
clock-frequency = <400000>;
pin-sda = <&gpio0 32 GPIO_FLAG_NONE>;
pin-scl = <&gpio0 33 GPIO_FLAG_NONE>;
};
spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
cs-gpios = <&gpio0 5 GPIO_FLAG_NONE>;
pin-mosi = <&gpio0 15 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 13 GPIO_FLAG_NONE>;
display {
compatible = "display-placeholder";
};
};
uart_grove: uart1 {
compatible = "espressif,esp32-uart";
status = "disabled";
port = <UART_NUM_1>;
pin-tx = <&gpio0 33 GPIO_FLAG_NONE>;
pin-rx = <&gpio0 32 GPIO_FLAG_NONE>;
};
};

Some files were not shown because too many files have changed in this diff Show More