Compare commits

...

38 Commits

Author SHA1 Message Date
Adolfo Reyna 2dc216fb7b fix: MCP settings persistence + WebServer/DevServer coexistence + screensaver charging toggle
- McpSettings was hardcoded to /data/service/mcp/settings.properties,
  but ES3C28P uses storage.userDataLocation=SD so user data lives on
  /sdcard/tactility/. Settings appeared to save but on reload returned
  default false -> "enable and when I go back shows disabled".
  Fixed to use getUserDataPath()+"/settings/mcp.properties" pattern
  consistent with DisplaySettings/WebServerSettings, with
  findOrCreateParentDirectory() and isFile() guard.

- HttpServer ctrl_port collision: ESP-IDF default 32768 used by both
  WebServer (80) and DevelopmentService (6666). Second httpd_start()
  failed silently -> dashboard up but /api/mcp 404 and :6666 refused.
  Fixed with unique ctrl_port = 32768 + (port % 1000).
  Added CONFIG_HTTPD_MAX_URI_HANDLERS=20 (default 8 < 9 needed for
  7 handlers + 2 internal).

- DevService stack 5120 -> 8192 for /app/install handling.
- McpSystem video stream task stack 4096 -> 8192 and idle yield 50ms
  (was 5ms always) to avoid CPU starvation blocking httpd tasks.
  On RLCD, timer must always run: DisplayIdle early return on
  !supportsBacklightDuty() broke MCP manual activation (cachedSettings
  not loaded, backlight duty 0). Fixed to always load settings +
  start timer, log RLCD detection, guard setBacklightDuty with
  supportsBacklightDuty() and fallback duty 255.

- DisplaySettings: add disableScreensaverWhenCharging bool, persisted,
  with UI toggle in Display app (Settings->Display) using
  PowerDevice::IsCharging check via findDevices callback.
  When charging and flag enabled, screensaver activation skipped,
  and if already dimmed, wake on charging.

- ES3C28P Power driver: GPIO9 ADC1_CH8 2x divider 2500-4200mV spec from
  ~/Downloads official docs, TP4054 CHRG not connected to GPIO, so
  IsCharging inferred via voltage thresholds (<500mV no battery USB
  or >5000mV wall powered, >=4150mV high) + rising trend.

Verified: flashed es3c28p to /dev/cu.usbmodem101 (192.168.68.113),
WiFi FamReynaMesh RSSI -59, dashboard 200, :6666/info now works,
/api/mcp returns disabled correctly until enabled, persists to SD.
2026-07-11 20:26:01 -04:00
Adolfo Reyna 81f289fc24 feat: declare i2s0 audio node in Waveshare RLCD devicetree 2026-07-10 19:11:44 -04:00
Adolfo Reyna d3919344b3 fix: explicitly add launcher buttons and power button to default group, declare stopScreensaverLocked, and disable idle screensaver timer on monochrome/RLCD screens 2026-07-10 18:54:37 -04:00
Adolfo Reyna 4ea29c0fe9 fix: remove click-focusable flag from container widgets and add high-contrast outline focus style to launcher buttons for monochrome display navigation 2026-07-10 18:46:07 -04:00
Adolfo Reyna e72d3998f9 fix: dynamically check and display active station (WIFI_STA_DEF) IP on MCP Settings view 2026-07-10 18:27:26 -04:00
Adolfo Reyna e423b28930 chore: default Waveshare RLCD board profile to boot straight to Launcher instead of ApWebServer 2026-07-10 18:25:36 -04:00
Adolfo Reyna 9ffd2051fe fix: resolve SD card mount error by routing schematic pins CLK=38, CMD=21, D0=39, D3=17 and disabling internal pull-up on CLK 2026-07-10 12:21:43 -04:00
Adolfo Reyna bd8fea0436 Fix display mirroring and update SDMMC card pins 2026-07-09 23:18:10 -04:00
Adolfo Reyna 301fed7f2e Fix active-low reset pin state in panel_st7305_reset 2026-07-09 23:14:19 -04:00
Adolfo Reyna da28b93f24 Update ST7305 display driver initialization to match rlcd.py settings 2026-07-09 23:09:13 -04:00
Adolfo Reyna b5f0575c5c Update SDMMC pins in Waveshare RLCD devicetree to match schematic 2026-07-09 23:02:06 -04:00
Adolfo Reyna c4ec7ead55 perf: Enable 512KB LVGL image cache on PSRAM and move modified lvgl to local components 2026-07-09 22:27:55 -04:00
Adolfo Reyna df93252281 feat: add support for waveshare esp32-s3-rlcd board 2026-07-09 21:44:49 -04:00
Adolfo Reyna e4b95396dd feat: Add JPEG decoder configuration and fix LCD color inversion 2026-07-08 23:20:06 -04:00
Adolfo Reyna 4003065074 fix(es3c28p): convert device.properties to dot-notation format to support Bluetooth in upstream build 2026-07-08 17:09:45 -04:00
Adolfo Reyna 300ddb3a7f feat: integrate MCP and ES3C28P audio support with upstream compatibility updates 2026-07-04 15:41:37 -04:00
Adolfo Reyna b8bf59fedf Merge remote-tracking branch 'origin/main' into main 2026-07-04 15:39:52 -04:00
Ken Van Hoeylandt ecad2248d9 Various improvements (#547)
- Fixed crash when returning to Setup app (from any of the steps)
- Fixed crash when returning to TimeDateSettings app (after locale selection)
- Fixed GuiService inconsistent behaviour: now always perform operations async (e.g. to show/hide apps). This fixes crashes in some onHide() calls where onHide() might be called before onShow() was called.
- Fix for incorrect WebServService path
- Remove CYD-4848S040C SD card functionality, but added a GPIO fix for releasing 3-wire SPI pin sharing.
- Fix for saving/loading settings for various apps
2026-07-04 21:26:43 +02:00
Ken Van Hoeylandt a323f8e148 Implement SD card for WaveShare S3 Touch LCD 4.3" (#546) 2026-07-04 09:28:31 +02:00
Ken Van Hoeylandt 05720821f8 Require SD card or >4MB flash (#545) 2026-07-03 23:56:03 +02:00
Ken Van Hoeylandt 90afba647e Update properties parsing for app manifest and device manifest files (#544) 2026-07-03 00:29:01 +02:00
Ken Van Hoeylandt 35fd7dd536 Post-release changes (#543)
- Update version to 0.8.0-dev
- Add new release scripts
- Update release docs
2026-07-02 20:52:26 +02:00
Ken Van Hoeylandt cd07c64926 Release 0.7.0 (#542) 2026-07-02 17:34:40 +02:00
Ken Van Hoeylandt 2f5e183f18 Setup app (#541)
- Implement Setup app for device's first boot
- Implement device simple name and vendor name variables
- Remove region setting (locale setting is already available)
2026-07-02 00:29:03 +02:00
Ken Van Hoeylandt d3439f6f45 Fixes & memory optimizations (#540)
- Disable BlueTooth driver by default, to ensure it doesn't allocate memory
- Update BlueTooth settings app behaviour to make sure it starts/stops the devices
- Disable SPI RAM usage for ST7789 as it broke App Hub
- Reduce T-Display and T-Deck display buffers from 1/3rd to 1/10th of resolution
- Enabled dynamic buffers for MbedTLS to optimize memory
- TLS 1.3 did not work with dynamic buffers: downgrade to 1.2 for now
- WiFi is now enabled from the Boot app callback to the WiFi service, instead of the WiFi service start() function. This avoids a lockup when WiFi is started by the service while the Boot app is updating the WiFi settings.
2026-07-01 21:11:46 +02:00
Adolfo Reyna 68a6ca0ce3 Associate .mp3 files with the Mp3Player application in Files browser 2026-06-28 22:23:01 -04:00
Adolfo Reyna 430f8889ac Merge remote-tracking branch 'origin/main' 2026-06-28 21:13:35 -04:00
Adolfo Reyna e0a92ea50e feat: migrate all large MCP server allocations and MP3 buffers to PSRAM (SPIRAM) 2026-06-27 10:04:30 -04:00
Adolfo Reyna 0ae9b43f87 fix: increase HTTP server stack size to 16KB to prevent MP3 decode stack overflow and reset I2S controller on stop to eliminate static noise 2026-06-27 10:00:36 -04:00
Adolfo Reyna 598af546fd fix: ensure backlight turns back on during startMcpScreensaver and invalidate canvas on drawText 2026-06-27 09:41:40 -04:00
Adolfo Reyna cd921e1aa9 feat: implement MCP drawing as screensaver instead of standalone app
- Add ScreensaverType::McpScreen to DisplaySettings enum, toString/fromString
- Add 'MCP Screen' option to the Display settings screensaver dropdown
- New McpScreensaver (Screensaver subclass): creates full-screen lv_canvas on
  the overlay, allocates framebuffer in SPIRAM, registers canvas pointer in
  McpSystemState, shows 'Waiting for LLM...' until first draw command arrives
- Wire McpScreensaver into DisplayIdle::activateScreensaver() switch
- McpSystem::ensureOverrideScreen() now calls displayidle::findService()->
  startScreensaver() instead of launching McpOverrideApp — canvas appears
  automatically on any LLM draw command
- Remove McpOverrideApp (replaced by screensaver), deregister from Tactility.cpp
- Remove mcpOverrideEnabled from McpSettings — no longer needed
- Simplify McpSettingsApp: remove Screen Override toggle, update info text to
  direct users to Settings -> Display -> Screensaver -> MCP Screen
2026-06-27 09:17:50 -04:00
Adolfo Reyna 41004a3c6f feat: integrate MCP server and screen override into system firmware
- Add McpSettings (load/save mcpEnabled, mcpOverrideEnabled to /data/service/mcp/settings.properties)
- Add McpSystem: global state, canvas drawing (RGB565, BMP, PBM, text), I2S audio (WAV, MP3 via minimp3, tone, record)
- Add McpHandler: unauthenticated POST /api/mcp (JSON-RPC 2.0, 13 tools) and POST /api/screen/raw endpoints
- Route MCP endpoints before Basic Auth check in WebServerService::handleApiPost
- Start HTTP server at boot if either webServerEnabled OR mcpEnabled is set
- Fix setEnabled: start unconditionally when called with true; only stop if both WS and MCP are disabled
- Add McpSettingsApp (Settings category): toggle MCP service + screen override, show live endpoint URL
- Add McpOverrideApp (appId one.tactility.mcpscreen): full-screen LVGL canvas for LLM-driven display override
- Register both new apps in Tactility.cpp
2026-06-26 23:02:09 -04:00
Adolfo Reyna 28ff01561a Fix mouse coordinates, sequence BLE discovery/encryption, and prevent mouse from disabling on-screen keyboard 2026-06-26 21:47:08 -04:00
Adolfo Reyna 68bf8f5a01 Revert "Fix BLE connection collision and filter unnamed devices"
This reverts commit bf7e40f0f1.
2026-06-26 13:40:20 -04:00
Adolfo Reyna bf7e40f0f1 Fix BLE connection collision and filter unnamed devices 2026-06-23 19:29:41 -04:00
Adolfo Reyna 71cc05e276 feat(es3c28p): fix audio speed-up, boost mic gain, and correct I2S pinout 2026-06-23 18:32:33 -04:00
Adolfo Reyna 94b5cdbfc4 feat(es3c28p): add audio playback and mic support, and fix chmod stub bug
Build Simulator / Linux (push) Has been cancelled
Build Simulator / macOS (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32 id:generic-esp32]) (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32c6 id:generic-esp32c6]) (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32p4 id:generic-esp32p4]) (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32s3 id:generic-esp32s3]) (push) Has been cancelled
Tests / TactilityTests (push) Has been cancelled
Tests / DevicetreeTests (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s024c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s024r]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s028r]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s028rv3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s032c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-3248s035c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-e32r28t]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-e32r32p]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:elecrow-crowpanel-basic-28]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:elecrow-crowpanel-basic-35]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:guition-jc2432w328c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:lilygo-tdisplay]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:m5stack-core2]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:m5stack-stickc-plus2]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:m5stack-stickc-plus]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32p4 id:guition-jc1060p470ciwy]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32p4 id:m5stack-tab5]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:btt-panda-touch]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:cyd-4848s040c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:cyd-8048s043c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:elecrow-crowpanel-advance-28]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:elecrow-crowpanel-advance-35]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:elecrow-crowpanel-advance-50]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:elecrow-crowpanel-basic-50]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:guition-jc3248w535c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:guition-jc8048w550c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:heltec-wifi-lora-32-v3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-tdeck]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-tdisplay-s3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-tdongle-s3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-thmi]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-tlora-pager]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-cardputer-adv]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-cardputer]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-cores3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-papers3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-stackchan]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-sticks3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:unphone]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-esp32-s3-geek]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-s3-lcd-13]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-s3-touch-lcd-128]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-s3-touch-lcd-147]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-s3-touch-lcd-43]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:wireless-tag-wt32-sc01-plus]) (push) Has been cancelled
Build Firmware / BundleArtifacts (push) Has been cancelled
Build Firmware / PublishFirmwareSnapshot (push) Has been cancelled
Build Firmware / PublishFirmwareStable (push) Has been cancelled
Build Firmware / PublishSdk (push) Has been cancelled
2026-06-23 12:34:32 -04:00
Adolfo Reyna 5c535490ea Fix compatibility issues for ESP-IDF v5.3.2 and add es3c28p target support
Build Simulator / Linux (push) Has been cancelled
Build Simulator / macOS (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32 id:generic-esp32]) (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32c6 id:generic-esp32c6]) (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32p4 id:generic-esp32p4]) (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32s3 id:generic-esp32s3]) (push) Has been cancelled
Tests / TactilityTests (push) Has been cancelled
Tests / DevicetreeTests (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s024c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s024r]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s028r]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s028rv3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s032c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-3248s035c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-e32r28t]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-e32r32p]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:elecrow-crowpanel-basic-28]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:elecrow-crowpanel-basic-35]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:guition-jc2432w328c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:lilygo-tdisplay]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:m5stack-core2]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:m5stack-stickc-plus2]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:m5stack-stickc-plus]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32p4 id:guition-jc1060p470ciwy]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32p4 id:m5stack-tab5]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:btt-panda-touch]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:cyd-4848s040c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:cyd-8048s043c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:elecrow-crowpanel-advance-28]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:elecrow-crowpanel-advance-35]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:elecrow-crowpanel-advance-50]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:elecrow-crowpanel-basic-50]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:guition-jc3248w535c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:guition-jc8048w550c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:heltec-wifi-lora-32-v3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-tdeck]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-tdisplay-s3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-tdongle-s3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-thmi]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-tlora-pager]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-cardputer-adv]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-cardputer]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-cores3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-papers3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-stackchan]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-sticks3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:unphone]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-esp32-s3-geek]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-s3-lcd-13]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-s3-touch-lcd-128]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-s3-touch-lcd-147]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-s3-touch-lcd-43]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:wireless-tag-wt32-sc01-plus]) (push) Has been cancelled
Build Firmware / BundleArtifacts (push) Has been cancelled
Build Firmware / PublishFirmwareSnapshot (push) Has been cancelled
Build Firmware / PublishFirmwareStable (push) Has been cancelled
Build Firmware / PublishSdk (push) Has been cancelled
2026-06-23 11:59:47 -04:00
4466 changed files with 936646 additions and 2159 deletions
+4
View File
@@ -11,6 +11,8 @@ jobs:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Build"
uses: ./.github/actions/build-simulator
with:
@@ -21,6 +23,8 @@ jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Build"
uses: ./.github/actions/build-simulator
with:
+4 -1
View File
@@ -23,6 +23,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Build SDK"
uses: ./.github/actions/build-sdk
with:
@@ -66,7 +68,6 @@ jobs:
{ 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 },
@@ -82,6 +83,8 @@ jobs:
needs: [ BuildSdk ]
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Build Firmware"
uses: ./.github/actions/build-firmware
with:
+2 -1
View File
@@ -31,8 +31,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: "Checkout repo"
uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive
- name: "Install Python Dependencies"
shell: bash
+21 -26
View File
@@ -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):
+135
View 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()
+126
View 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()
+2 -5
View File
@@ -28,7 +28,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 +36,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 +46,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()
+84
View 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()
+18 -1
View File
@@ -2,6 +2,8 @@
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3072
# Ensure large enough stack for network operations
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
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
# Free up IRAM
@@ -9,7 +11,8 @@ CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y
CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y
# EmbedTLS
CONFIG_MBEDTLS_SSL_PROTO_TLS1_3=y
# Use TLS 1.2 because 1.3 conflicts with MbedTLS dynamic buffer
CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y
# LVGL
CONFIG_LV_USE_USER_DATA=y
CONFIG_LV_USE_FS_STDIO=y
@@ -40,3 +43,17 @@ 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
# 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
+8 -2
View File
@@ -54,6 +54,8 @@ if (DEFINED ENV{ESP_IDF_VERSION})
set(EXCLUDE_COMPONENTS "Simulator")
idf_build_set_property(LINK_OPTIONS "-Wl,--allow-multiple-definition" APPEND)
# Panic handler wrapping is only available on Xtensa architecture
if (CONFIG_IDF_TARGET_ARCH_XTENSA)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND)
@@ -72,8 +74,11 @@ else ()
message("Building for sim target")
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_AUTO_START_APP_ID="")
add_compile_definitions(CONFIG_TT_USER_DATA_LOCATION_INTERNAL)
endif ()
project(Tactility)
@@ -101,8 +106,9 @@ if (NOT DEFINED ENV{ESP_IDF_VERSION})
target_compile_definitions(freertos_kernel PUBLIC "projCOVERAGE_TEST=0")
# EmbedTLS
set(ENABLE_TESTING OFF)
set(ENABLE_PROGRAMS OFF)
set(ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(ENABLE_PROGRAMS OFF CACHE BOOL "" FORCE)
set(MBEDTLS_FATAL_WARNINGS OFF CACHE BOOL "" FORCE)
add_subdirectory(Libraries/mbedtls)
# SDL
@@ -1 +0,0 @@
enableOnBoot=false
@@ -1 +0,0 @@
enableOnBoot=false
@@ -1 +0,0 @@
enableOnBoot=false
-5
View File
@@ -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>
-63
View File
@@ -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>
-3
View File
@@ -1,3 +0,0 @@
{
"version": 0
}
@@ -13,6 +13,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
+18 -21
View File
@@ -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=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
+12 -15
View File
@@ -1,19 +1,16 @@
[general]
vendor=CYD
name=2432S024C
general.vendor=CYD
general.name=2432S024C
[apps]
launcherAppId=Launcher
apps.launcherAppId=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
+12 -15
View File
@@ -1,19 +1,16 @@
[general]
vendor=CYD
name=2432S024R
general.vendor=CYD
general.name=2432S024R
[apps]
launcherAppId=Launcher
apps.launcherAppId=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
+13 -17
View File
@@ -1,22 +1,18 @@
[general]
vendor=CYD
name=2432S028R
general.vendor=CYD
general.name=2432S028R
[apps]
launcherAppId=Launcher
apps.launcherAppId=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
cdn.warningMessage=There are 3 hardware variants of this board. This build works on the original variant only ("v1").
lvgl.colorDepth=16
+13 -17
View File
@@ -1,22 +1,18 @@
[general]
vendor=CYD
name=2432S028R v3
general.vendor=CYD
general.name=2432S028R v3
[apps]
launcherAppId=Launcher
apps.launcherAppId=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 only supports board version 3.
display.size=2.8"
display.shape=rectangle
display.dpi=143
[lvgl]
colorDepth=16
cdn.warningMessage=There are 3 hardware variants of this board. This build only supports board version 3.
lvgl.colorDepth=16
+12 -15
View File
@@ -1,19 +1,16 @@
[general]
vendor=CYD
name=2432S032C
general.vendor=CYD
general.name=2432S032C
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32
flashSize=4MB
spiRam=false
hardware.target=ESP32
hardware.flashSize=4MB
hardware.spiRam=false
[display]
size=3.2"
shape=rectangle
dpi=125
storage.userDataLocation=SD
[lvgl]
colorDepth=16
display.size=3.2"
display.shape=rectangle
display.dpi=125
lvgl.colorDepth=16
+12 -15
View File
@@ -1,19 +1,16 @@
[general]
vendor=CYD
name=3248S035C
general.vendor=CYD
general.name=3248S035C
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32
flashSize=4MB
spiRam=false
hardware.target=ESP32
hardware.flashSize=4MB
hardware.spiRam=false
[display]
size=3.5"
shape=rectangle
dpi=165
storage.userDataLocation=SD
[lvgl]
colorDepth=16
display.size=3.5"
display.shape=rectangle
display.dpi=165
lvgl.colorDepth=16
@@ -12,9 +12,25 @@
#include <esp_lcd_panel_ops.h>
#include <esp_lcd_panel_io_additions.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");
// 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[] = {
// {cmd, { data }, data_size, delay_ms}
{0xFF, (uint8_t[]) {0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
@@ -181,6 +197,8 @@ bool St7701Display::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lc
return false;
}
reclaimSpiPinsForSdCard();
return true;
}
+1 -1
View File
@@ -12,7 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
struct Module cyd_4848s040c_module = {
Module cyd_4848s040c_module = {
.name = "cyd-4848s040c",
.start = start,
.stop = stop,
+1 -14
View File
@@ -13,6 +13,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
@@ -27,18 +28,4 @@
pin-sda = <&gpio0 19 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>;
};
};
};
+15 -18
View File
@@ -1,22 +1,19 @@
[general]
vendor=CYD
name=4848S040C
general.vendor=CYD
general.name=4848S040C
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=16MB
spiRam=true
spiRamMode=OCT
spiRamSpeed=80M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=80M
hardware.bluetooth=true
[display]
size=4"
shape=rectangle
dpi=170
storage.userDataLocation=SD
[lvgl]
colorDepth=16
display.size=4"
display.shape=rectangle
display.dpi=170
lvgl.colorDepth=16
+1
View File
@@ -14,6 +14,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
+21 -25
View File
@@ -1,30 +1,26 @@
[general]
vendor=CYD
name=8048S043C
incubating=false
general.vendor=CYD
general.name=8048S043C
general.incubating=false
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=16MB
spiRam=true
spiRamMode=OCT
spiRamSpeed=80M
esptoolFlashFreq=80M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=80M
hardware.esptoolFlashFreq=80M
hardware.bluetooth=true
[display]
size=4.3"
shape=rectangle
dpi=217
storage.userDataLocation=SD
[cdn]
infoMessage=
warningMessage=
display.size=4.3"
display.shape=rectangle
display.dpi=217
[lvgl]
theme=DefaultDark
colorDepth=16
fontSize=18
cdn.infoMessage=
cdn.warningMessage=
lvgl.theme=DefaultDark
lvgl.colorDepth=16
lvgl.fontSize=18
+12 -15
View File
@@ -1,19 +1,16 @@
[general]
vendor=CYD
name=E32R28T
general.vendor=CYD
general.name=E32R28T
[apps]
launcherAppId=Launcher
apps.launcherAppId=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
[lvgl]
colorDepth=16
display.size=2.8"
display.shape=rectangle
display.dpi=143
lvgl.colorDepth=16
+12 -15
View File
@@ -1,19 +1,16 @@
[general]
vendor=CYD
name=E32R32P
general.vendor=CYD
general.name=E32R32P
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32
flashSize=4MB
spiRam=false
hardware.target=ESP32
hardware.flashSize=4MB
hardware.spiRam=false
[display]
size=2.8"
shape=rectangle
dpi=125
storage.userDataLocation=SD
[lvgl]
colorDepth=16
display.size=2.8"
display.shape=rectangle
display.dpi=125
lvgl.colorDepth=16
@@ -1,24 +1,21 @@
[general]
vendor=Elecrow
name=CrowPanel Advance 2.8"
general.vendor=Elecrow
general.name=CrowPanel Advance 2.8"
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=16MB
spiRam=true
spiRamMode=OCT
spiRamSpeed=120M
tinyUsb=true
esptoolFlashFreq=120M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=120M
hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
[display]
size=2.8"
shape=rectangle
dpi=143
storage.userDataLocation=SD
[lvgl]
colorDepth=16
display.size=2.8"
display.shape=rectangle
display.dpi=143
lvgl.colorDepth=16
@@ -15,6 +15,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
@@ -1,24 +1,21 @@
[general]
vendor=Elecrow
name=CrowPanel Advance 3.5"
general.vendor=Elecrow
general.name=CrowPanel Advance 3.5"
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=16MB
spiRam=true
spiRamMode=OCT
spiRamSpeed=120M
tinyUsb=true
esptoolFlashFreq=120M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=120M
hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
[display]
size=3.5"
shape=rectangle
dpi=165
storage.userDataLocation=SD
[lvgl]
colorDepth=16
display.size=3.5"
display.shape=rectangle
display.dpi=165
lvgl.colorDepth=16
@@ -15,6 +15,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
@@ -1,25 +1,22 @@
[general]
vendor=Elecrow
name=CrowPanel Advance 5.0"
general.vendor=Elecrow
general.name=CrowPanel Advance 5.0"
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=16MB
spiRam=true
spiRamMode=OCT
spiRamSpeed=120M
tinyUsb=true
esptoolFlashFreq=120M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=120M
hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
[display]
size=5"
shape=rectangle
dpi=187
storage.userDataLocation=SD
[lvgl]
colorDepth=16
fontSize=18
display.size=5"
display.shape=rectangle
display.dpi=187
lvgl.colorDepth=16
lvgl.fontSize=18
@@ -14,6 +14,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
@@ -1,19 +1,16 @@
[general]
vendor=Elecrow
name=CrowPanel Basic 2.8"
general.vendor=Elecrow
general.name=CrowPanel Basic 2.8"
[apps]
launcherAppId=Launcher
apps.launcherAppId=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
[lvgl]
colorDepth=16
display.size=2.8"
display.shape=rectangle
display.dpi=143
lvgl.colorDepth=16
@@ -1,19 +1,16 @@
[general]
vendor=Elecrow
name=CrowPanel Basic 3.5"
general.vendor=Elecrow
general.name=CrowPanel Basic 3.5"
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32
flashSize=4MB
spiRam=false
hardware.target=ESP32
hardware.flashSize=4MB
hardware.spiRam=false
[display]
size=3.5"
shape=rectangle
dpi=165
storage.userDataLocation=SD
[lvgl]
colorDepth=16
display.size=3.5"
display.shape=rectangle
display.dpi=165
lvgl.colorDepth=16
@@ -1,25 +1,22 @@
[general]
vendor=Elecrow
name=CrowPanel Basic 5.0"
general.vendor=Elecrow
general.name=CrowPanel Basic 5.0"
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=4MB
spiRam=true
spiRamMode=OCT
spiRamSpeed=120M
tinyUsb=true
esptoolFlashFreq=120M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=4MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=120M
hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
[display]
size=5.0"
shape=rectangle
dpi=187
storage.userDataLocation=SD
[lvgl]
colorDepth=16
fontSize=18
display.size=5.0"
display.shape=rectangle
display.dpi=187
lvgl.colorDepth=16
lvgl.fontSize=18
@@ -14,6 +14,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
@@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
idf_component_register(
SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source"
REQUIRES Tactility esp_lvgl_port esp_lcd AXP192 ST7789 ButtonControl
REQUIRES Tactility esp_lvgl_port ILI934x FT6x36 PwmBacklight driver vfs fatfs esp_adc
)
+94
View File
@@ -0,0 +1,94 @@
#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
};
@@ -0,0 +1,49 @@
#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);
}
@@ -1,18 +1,20 @@
#pragma once
#include <Tactility/hal/display/DisplayDevice.h>
#include <memory>
#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_5;
constexpr auto LCD_PIN_DC = GPIO_NUM_23;
constexpr auto LCD_PIN_RESET = GPIO_NUM_18;
constexpr auto LCD_HORIZONTAL_RESOLUTION = 135;
constexpr auto LCD_PIN_CS = GPIO_NUM_10;
constexpr auto LCD_PIN_DC = GPIO_NUM_46;
constexpr auto LCD_HORIZONTAL_RESOLUTION = 320;
constexpr auto LCD_VERTICAL_RESOLUTION = 240;
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 3;
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 10;
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();
+156
View File
@@ -0,0 +1,156 @@
#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
@@ -0,0 +1,5 @@
#pragma once
#include <Tactility/hal/power/PowerDevice.h>
#include <memory>
std::shared_ptr<tt::hal::power::PowerDevice> createPower();
@@ -12,8 +12,8 @@ static error_t stop() {
return ERROR_NONE;
}
struct Module m5stack_stickc_plus_module = {
.name = "m5stack-stickc-plus",
struct Module es3c28p_module = {
.name = "es3c28p",
.start = start,
.stop = stop,
.symbols = nullptr,
+25
View File
@@ -0,0 +1,25 @@
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
@@ -0,0 +1,3 @@
dependencies:
- Platforms/platform-esp32
dts: es3c28p.dts
+81
View File
@@ -0,0 +1,81 @@
/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";
};
};
};
+7 -9
View File
@@ -1,12 +1,10 @@
[general]
vendor=Generic
name=ESP32
general.vendor=Generic
general.name=ESP32
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32
flashSize=4MB
spiRam=false
hardware.target=ESP32
hardware.flashSize=8MB
hardware.spiRam=false
storage.userDataLocation=Internal
+7 -9
View File
@@ -1,12 +1,10 @@
[general]
vendor=Generic
name=ESP32-C6
general.vendor=Generic
general.name=ESP32-C6
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32C6
flashSize=4MB
spiRam=false
hardware.target=ESP32C6
hardware.flashSize=8MB
hardware.spiRam=false
storage.userDataLocation=Internal
+7 -9
View File
@@ -1,12 +1,10 @@
[general]
vendor=Generic
name=ESP32-P4
general.vendor=Generic
general.name=ESP32-P4
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32P4
flashSize=4MB
spiRam=false
hardware.target=ESP32P4
hardware.flashSize=8MB
hardware.spiRam=false
storage.userDataLocation=Internal
+7 -9
View File
@@ -1,12 +1,10 @@
[general]
vendor=Generic
name=ESP32-S3
general.vendor=Generic
general.name=ESP32-S3
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=4MB
spiRam=false
hardware.target=ESP32S3
hardware.flashSize=8MB
hardware.spiRam=false
storage.userDataLocation=Internal
@@ -1,33 +1,29 @@
[general]
vendor=Guition
name=JC1060P470C-I-W-Y
general.vendor=Guition
general.name=JC1060P470C-I-W-Y
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32P4
flashSize=16MB
spiRam=true
spiRamMode=OCT
spiRamSpeed=200M
esptoolFlashFreq=80M
bluetooth=true
hardware.target=ESP32P4
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=200M
hardware.esptoolFlashFreq=80M
hardware.bluetooth=true
[display]
size=7"
shape=rectangle
dpi=187
storage.userDataLocation=SD
[lvgl]
colorDepth=16
display.size=7"
display.shape=rectangle
display.dpi=187
[sdkconfig]
CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30
CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
CONFIG_ESP_HOSTED_ENABLED=y
CONFIG_ESP_HOSTED_P4_DEV_BOARD_FUNC_BOARD=y
CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE=y
CONFIG_SLAVE_IDF_TARGET_ESP32C6=y
CONFIG_ESP_HOSTED_USE_MEMPOOL=n
lvgl.colorDepth=16
sdkconfig.CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
sdkconfig.CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30
sdkconfig.CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
sdkconfig.CONFIG_ESP_HOSTED_ENABLED=y
sdkconfig.CONFIG_ESP_HOSTED_P4_DEV_BOARD_FUNC_BOARD=y
sdkconfig.CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE=y
sdkconfig.CONFIG_SLAVE_IDF_TARGET_ESP32C6=y
sdkconfig.CONFIG_ESP_HOSTED_USE_MEMPOOL=n
@@ -21,6 +21,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
+12 -15
View File
@@ -1,19 +1,16 @@
[general]
vendor=Guition
name=JC2432W328C
general.vendor=Guition
general.name=JC2432W328C
[apps]
launcherAppId=Launcher
apps.launcherAppId=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
[lvgl]
colorDepth=16
display.size=2.8"
display.shape=rectangle
display.dpi=143
lvgl.colorDepth=16
+17 -20
View File
@@ -1,24 +1,21 @@
[general]
vendor=Guition
name=JC3248W535C
general.vendor=Guition
general.name=JC3248W535C
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=16MB
spiRam=true
spiRamMode=OCT
spiRamSpeed=120M
tinyUsb=true
esptoolFlashFreq=120M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=120M
hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
[display]
size=3.5"
shape=rectangle
dpi=165
storage.userDataLocation=SD
[lvgl]
colorDepth=16
display.size=3.5"
display.shape=rectangle
display.dpi=165
lvgl.colorDepth=16
@@ -16,6 +16,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
+17 -20
View File
@@ -1,24 +1,21 @@
[general]
vendor=Guition
name=JC8048W550C
general.vendor=Guition
general.name=JC8048W550C
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=16MB
spiRam=true
spiRamMode=OCT
spiRamSpeed=80M
esptoolFlashFreq=80M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=80M
hardware.esptoolFlashFreq=80M
hardware.bluetooth=true
[display]
size=5"
shape=rectangle
dpi=187
storage.userDataLocation=SD
[lvgl]
colorDepth=16
fontSize=18
display.size=5"
display.shape=rectangle
display.dpi=187
lvgl.colorDepth=16
lvgl.fontSize=18
@@ -15,6 +15,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
@@ -1,30 +1,26 @@
[general]
vendor=Heltec
name=WiFi LoRa 32 v3
incubating=true
general.vendor=Heltec
general.name=WiFi LoRa 32 v3
general.incubating=true
[apps]
launcherAppId=Launcher
autoStartAppId=ApWebServer
apps.launcherAppId=Launcher
apps.autoStartAppId=ApWebServer
[hardware]
target=ESP32S3
flashSize=8MB
spiRam=false
tinyUsb=true
esptoolFlashFreq=120M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=8MB
hardware.spiRam=false
hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
[display]
size=0.96"
shape=rectangle
dpi=149
storage.userDataLocation=Internal
[cdn]
infoMessage=Due to the small size of the screen, the icons don't render properly.
display.size=0.96"
display.shape=rectangle
display.dpi=149
[lvgl]
theme=Mono
colorDepth=16
uiScale=70
uiDensity=compact
cdn.infoMessage=Due to the small size of the screen, the icons don't render properly.
lvgl.theme=Mono
lvgl.colorDepth=16
lvgl.uiScale=70
lvgl.uiDensity=compact
@@ -11,6 +11,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
@@ -38,7 +38,7 @@ std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
.resetPin = GPIO_NUM_NC,
.lvglSwapBytes = false,
.buffSpiram = true
.buffSpiram = false // Enabling leads to crashes when refreshing App Hub list
};
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_HORIZONTAL_RESOLUTION = 320;
constexpr auto LCD_VERTICAL_RESOLUTION = 240;
constexpr auto LCD_BUFFER_HEIGHT = (LCD_VERTICAL_RESOLUTION / 3);
constexpr auto LCD_BUFFER_HEIGHT = (LCD_VERTICAL_RESOLUTION / 10);
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;
+18 -22
View File
@@ -1,27 +1,23 @@
[general]
vendor=LilyGO
name=T-Deck,T-Deck Plus
general.vendor=LilyGO
general.name=T-Deck,T-Deck Plus
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=16MB
spiRam=true
spiRamMode=OCT
spiRamSpeed=120M
tinyUsb=true
esptoolFlashFreq=120M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=120M
hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
[display]
size=2.8"
shape=rectangle
dpi=143
storage.userDataLocation=SD
[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.
display.size=2.8"
display.shape=rectangle
display.dpi=143
[lvgl]
colorDepth=16
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
+1
View File
@@ -18,6 +18,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
+18 -21
View File
@@ -1,25 +1,22 @@
[general]
vendor=LilyGO
name=T-Display S3
general.vendor=LilyGO
general.name=T-Display S3
[apps]
launcherAppId=Launcher
autoStartAppId=ApWebServer
apps.launcherAppId=Launcher
apps.autoStartAppId=ApWebServer
[hardware]
target=ESP32S3
flashSize=16MB
spiRam=true
spiRamMode=OCT
spiRamSpeed=120M
tinyUsb=true
esptoolFlashFreq=120M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=120M
hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
[display]
size=1.9"
shape=rectangle
dpi=191
storage.userDataLocation=Internal
[lvgl]
colorDepth=16
display.size=1.9"
display.shape=rectangle
display.dpi=191
lvgl.colorDepth=16
@@ -10,6 +10,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
@@ -12,7 +12,7 @@ constexpr auto LCD_PIN_RESET = GPIO_NUM_23;
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_4;
constexpr auto LCD_HORIZONTAL_RESOLUTION = 135;
constexpr auto LCD_VERTICAL_RESOLUTION = 240;
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 3;
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 6;
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
+16 -19
View File
@@ -1,23 +1,20 @@
[general]
vendor=LilyGO
name=T-Display
incubating=true
general.vendor=LilyGO
general.name=T-Display
general.incubating=true
[apps]
launcherAppId=Launcher
autoStartAppId=ApWebServer
apps.launcherAppId=Launcher
apps.autoStartAppId=ApWebServer
[hardware]
target=ESP32
flashSize=16MB
spiRam=false
esptoolFlashFreq=80M
hardware.target=ESP32
hardware.flashSize=16MB
hardware.spiRam=false
hardware.esptoolFlashFreq=80M
[display]
size=1.14"
shape=rectangle
dpi=242
storage.userDataLocation=Internal
[lvgl]
colorDepth=16
uiDensity=compact
display.size=1.14"
display.shape=rectangle
display.dpi=242
lvgl.colorDepth=16
lvgl.uiDensity=compact
+19 -22
View File
@@ -1,26 +1,23 @@
[general]
vendor=LilyGO
name=T-Dongle S3
incubating=true
general.vendor=LilyGO
general.name=T-Dongle S3
general.incubating=true
[apps]
launcherAppId=Launcher
autoStartAppId=ApWebServer
apps.launcherAppId=Launcher
apps.autoStartAppId=ApWebServer
[hardware]
target=ESP32S3
flashSize=16MB
spiRam=false
tinyUsb=true
esptoolFlashFreq=120M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=false
hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
[display]
size=0.96"
shape=rectangle
dpi=186
storage.userDataLocation=SD
[lvgl]
colorDepth=16
uiDensity=compact
fontSize=10
display.size=0.96"
display.shape=rectangle
display.dpi=186
lvgl.colorDepth=16
lvgl.uiDensity=compact
lvgl.fontSize=10
@@ -15,6 +15,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
+17 -20
View File
@@ -1,24 +1,21 @@
[general]
vendor=LilyGO
name=T-HMI
general.vendor=LilyGO
general.name=T-HMI
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=16MB
spiRam=true
spiRamMode=OCT
spiRamSpeed=120M
tinyUsb=true
esptoolFlashFreq=120M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=120M
hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
[display]
size=2.8"
shape=rectangle
dpi=125
storage.userDataLocation=SD
[lvgl]
colorDepth=16
display.size=2.8"
display.shape=rectangle
display.dpi=125
lvgl.colorDepth=16
+1
View File
@@ -15,6 +15,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
+19 -22
View File
@@ -1,26 +1,23 @@
[general]
vendor=LilyGO
name=T-Lora Pager
general.vendor=LilyGO
general.name=T-Lora Pager
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=16MB
flashMode=DIO
spiRam=true
spiRamMode=AUTO
spiRamSpeed=120M
tinyUsb=true
esptoolFlashFreq=40M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.flashMode=DIO
hardware.spiRam=true
hardware.spiRamMode=AUTO
hardware.spiRamSpeed=120M
hardware.tinyUsb=true
hardware.esptoolFlashFreq=40M
hardware.bluetooth=true
[display]
size=2.33"
shape=rectangle
dpi=227
storage.userDataLocation=SD
[lvgl]
colorDepth=16
dpi=150
display.size=2.33"
display.shape=rectangle
display.dpi=227
lvgl.colorDepth=16
lvgl.dpi=150
@@ -17,6 +17,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
+16 -19
View File
@@ -1,24 +1,21 @@
[general]
vendor=M5Stack
name=Cardputer Adv
general.vendor=M5Stack
general.name=Cardputer Adv
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=8MB
spiRam=false
tinyUsb=true
esptoolFlashFreq=120M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=8MB
hardware.spiRam=false
hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
[display]
size=1.14"
shape=rectangle
storage.userDataLocation=SD
display.size=1.14"
display.shape=rectangle
# TODO: dps is actually 242, but this breaks UI (button selection becomes invisible and switch visibility is reduced)
dpi=139
display.dpi=139
[lvgl]
colorDepth=16
uiDensity=compact
lvgl.colorDepth=16
lvgl.uiDensity=compact
@@ -19,6 +19,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
+16 -19
View File
@@ -1,24 +1,21 @@
[general]
vendor=M5Stack
name=Cardputer,Cardputer v1.1
general.vendor=M5Stack
general.name=Cardputer,Cardputer v1.1
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=8MB
spiRam=false
tinyUsb=true
esptoolFlashFreq=120M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=8MB
hardware.spiRam=false
hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
[display]
size=1.14"
shape=rectangle
storage.userDataLocation=SD
display.size=1.14"
display.shape=rectangle
# TODO: dps is actually 242, but this breaks UI (button selection becomes invisible and switch visibility is reduced)
dpi=139
display.dpi=139
[lvgl]
colorDepth=16
uiDensity=compact
lvgl.colorDepth=16
lvgl.uiDensity=compact
@@ -18,6 +18,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
+16 -20
View File
@@ -1,25 +1,21 @@
[general]
vendor=M5Stack
name=Core2
general.vendor=M5Stack
general.name=Core2
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32
flashSize=16MB
spiRam=true
spiRamMode=QUAD
spiRamSpeed=80M
esptoolFlashFreq=80M
hardware.target=ESP32
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=QUAD
hardware.spiRamSpeed=80M
hardware.esptoolFlashFreq=80M
[display]
size=2"
shape=rectangle
dpi=200
storage.userDataLocation=SD
[cdn]
warningMessage=This board implementation concerns the original Core2 hardware and **not** the v1.1 variant
display.size=2"
display.shape=rectangle
display.dpi=200
[lvgl]
colorDepth=16
cdn.warningMessage=This board implementation concerns the original Core2 hardware and **not** the v1.1 variant
lvgl.colorDepth=16
+17 -20
View File
@@ -1,24 +1,21 @@
[general]
vendor=M5Stack
name=CoreS3
general.vendor=M5Stack
general.name=CoreS3
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=16MB
spiRam=true
spiRamMode=QUAD
spiRamSpeed=120M
tinyUsb=true
esptoolFlashFreq=120M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=QUAD
hardware.spiRamSpeed=120M
hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
[display]
size=2"
shape=rectangle
dpi=200
storage.userDataLocation=SD
[lvgl]
colorDepth=16
display.size=2"
display.shape=rectangle
display.dpi=200
lvgl.colorDepth=16
@@ -20,6 +20,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
+20 -26
View File
@@ -1,32 +1,26 @@
[general]
vendor=M5Stack
name=PaperS3
incubating=true
general.vendor=M5Stack
general.name=PaperS3
general.incubating=true
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=esp32s3
flashSize=16MB
spiRam=true
spiRamMode=OPI
spiRamSpeed=80M
esptoolFlashFreq=80M
tinyUsb=true
bluetooth=true
hardware.target=esp32s3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OPI
hardware.spiRamSpeed=80M
hardware.esptoolFlashFreq=80M
hardware.tinyUsb=true
hardware.bluetooth=true
[display]
size=4.7"
shape=rectangle
dpi=235
storage.userDataLocation=SD
[lvgl]
colorDepth=8
fontSize=24
theme=Mono
[sdkconfig]
CONFIG_EPD_DISPLAY_TYPE_ED047TC2=y
display.size=4.7"
display.shape=rectangle
display.dpi=235
lvgl.colorDepth=8
lvgl.fontSize=24
lvgl.theme=Mono
sdkconfig.CONFIG_EPD_DISPLAY_TYPE_ED047TC2=y
@@ -15,6 +15,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
+17 -20
View File
@@ -1,24 +1,21 @@
[general]
vendor=M5Stack
name=StackChan
general.vendor=M5Stack
general.name=StackChan
[apps]
launcherAppId=Launcher
apps.launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=16MB
spiRam=true
spiRamMode=QUAD
spiRamSpeed=120M
tinyUsb=true
esptoolFlashFreq=120M
bluetooth=true
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=QUAD
hardware.spiRamSpeed=120M
hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
[display]
size=2"
shape=rectangle
dpi=200
storage.userDataLocation=SD
[lvgl]
colorDepth=16
display.size=2"
display.shape=rectangle
display.dpi=200
lvgl.colorDepth=16
@@ -22,6 +22,7 @@
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
@@ -1,29 +0,0 @@
#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
};
@@ -1,67 +0,0 @@
#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,39 +0,0 @@
#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;
});
}
@@ -1,9 +0,0 @@
#pragma once
#include <Axp192.h>
bool initAxp();
// Must call initAxp() first before this returns a non-nullptr response
std::shared_ptr<Axp192> getAxp192();
@@ -1,23 +0,0 @@
[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
@@ -1,5 +0,0 @@
dependencies:
- Platforms/platform-esp32
- Drivers/mpu6886-module
- Drivers/bm8563-module
dts: m5stack,stickc-plus.dts
@@ -1,66 +0,0 @@
/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