Fix button background restore: save/restore original Drawable instead of tint list null -> gray -> teal → grijs blijft grijs na loslaten

This commit is contained in:
eddy 2026-07-29 20:29:30 +02:00
commit 817f9a0602
6 changed files with 872 additions and 6 deletions

View file

@ -0,0 +1,45 @@
DC3-24V Capacitieve Touch Switch Sensor Module Momentary/Vergrendeling 10A Regelbare Stroom Elektronische Component 137 verkocht €1,06 46% korting €1,97 Meer dan 3 stuks, 1% extra kortingo ff Pr ij s is inclusief btw | Douanerechten worden berekend b ij het afrekenen ; Extra 2% korting €2,00 korting op €15,00 AI-overzicht van item Disclaimer: deze inhoud is door AI gegenereerd en vertegenwoordigt niet de mening van de verkoper. Het platform en de verkopers aanvaarden geen enkele juridische aansprakel ij kheid in dit verband. DC3-24V capaciteve touch sensor module Deze DC3-24V capacitive touch switch sensor module is ontworpen voor gebruik in elektronische systemen met een spanning van 3 tot 24 V. Ideaal voor automatiseringstoepassingen waar betrouwbare bediening nodig is. Momentary en vergrendelingsmodus De module ondersteunt zowel momentary als vergrendelingsmodus , waardoor flexibele bediening mogel ij k is. Perfect voor toepassingen waar een t ij del ij ke of bl ij vende activering vereist is. Maximaal 10 A stroomregeling Met een maximale stroomcapaciteit van 10 A kan de module zware elektrische belastingen aan. Geschikt voor het schakelen van motoren, verlichting of andere elektrische apparaten in automatiseringssystemen. CE-gecerti fi ceerd en veilig gebruik De module is CE-gecerti fi ceerd , wat aangeeft dat deze voldoet aan Europese veiligheids- en prestatie-eisen. Zorgt voor betrouwbare en veilige werking in diverse omgevingen. Compacte afmetingen en licht gewicht Met een verpakking van 2 x 2 x 1 cm en een gewicht van 0,011 kg is de module compact en licht. Ideaal voor ruimtebeperkte installaties of mobiele systemen. Meer pr ij sinformatie Beoordeling Gerelateerde items Bundel : 3-24V Bek ij k Meer 5 V-24 V 3A d … € 3 , 23 €7,18 -55% 4.8 €2 korting op €15 Dc 3V-15V Ca … € 3 , 27 5 Gratis levering vanaf €1 DC 5 V-30 V T … € 1 , 51 4.6 Bestverkopend op A DC 5 V-30 V T … € 1 , 46 5 Gratis levering vanaf €1 TTP223B Digit … € 2 , 27 €4,54 -50% 120 verkocht €2 korting op €15 ad. Download de AliExpress- app Hi, Eddy Account hc12 433mhz NL / EUR … Winkelwagentje
Speci fi caties Overzicht report Elk €3 07 ≥ 2 stuks Beste pr ij s voor verg Gratis levering vanaf €1 Elk €2 16 ≥ 3 stuks Gebruik anderen Hoogwaardige chemicaliën Geen Communicatiemethode andere Camera uitgerust Nee Modelnummer DC3-24V Compatibiliteit anderen Merknaam occkic Oorsprong Cn (Oorsprong) Staat van montage B ij na klaar Speci fi catie CN-stekker Itemgidsen en documenten Gebruikershandleiding Bek ij ken Kenmerken 1.Brede spanningscompatibiliteit 3-24V, ultralaag standby-stroomverbruik slechts 2μA 2.10A krachtige aandr ij ving, stabiele controle voor krachtige apparaten 3.Dubbele t ij del ij ke/vergrendelingsmodi, flexibele aanpassing aan toepassingsbehoeften 4.Niet-metalen paneeldetratiedetectie, nauwkeurige herkenning door glas/kunststof 5.MOSFET-ontwerp van industriële kwaliteit, sterk anti-interferentievermogen en lange levensduur Productbeschr ij ving Deze module maakt gebruik van de JR223B capacitieve aanraaksensorchip + SI4164 krachtige MOSFET-uitgang, die 3-24V brede ingangsspanningsregeling ondersteunt.De statische stroom van de module is ≤2μA wanneer de schakelaar is uitgeschakeld, en de schakelaar kan een maximale stroom van maximaal 10A regelen.Vaak gebruikt voor aanraakdetectiecontrole op niet-metalen materialen zoals plastic, glas, rubber, acrylpanelen of droge houten oppervlakken.Toepassingen z ij n onder meer aanraakschakelaars, LED-lichtmodi fi caties, auto-aanpassing, ontsnappingsruimtespellen en andere aanraakgestuurde producten. Parameter Bedr ij fsspanning: DC 3-24V Maximale regelbare stroom: 10A Stroomverbruik in stand-by: ≤2 μA Aanraakdetectieafstand: ≤8 mm (kan enigszins variëren afhankel ij k van het materiaal van het afdekpaneel) Voorzorgsmaatregelen: 1.Deze module werkt binnen een spanningsbereik van DC 3-24V.Zorg ervoor dat de voedingsspanning binnen dit bereik valt; overmatige spanning kan de module beschadigen. 2.Bevestig de module aan de binnenkant van het paneel met dubbelz ij dige tape.Zorg ervoor dat er geen luchtspleet bestaat tussen de aanraakmodule en het paneel, aangezien dit de aanraakgevoeligheid en detectieafstand kan beïnvloeden. 3.Wanneer het op glazen spiegels wordt aangebracht, isoleer het verzilverde aanraakgevoelige gebied van de non- touch coating.4.Verm ij d het verminderen van de capaciteit op punt A om de gevoeligheid te verhogen.Minimaliseer in Klantenrecensies Speci fi caties Overzicht Winkel Dit vindt u misschien ook leuk Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
plaats daarvan de paneeldikte en openingen tussen schakelaars en panelen.Hoewel dit de gevoeligheid kan verbeteren, vergroot het ook de kans op valse triggers. 5.Volg strikt het referentieschakelschema voor bedrading. Pakket omvat Module*1 Dubbelz ij dige tape*1 Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Informatie over b ij komende kosten Voor items die van buiten de Europese Unie worden geleverd, kunt u onderworpen z ij n aan extra kosten voor btw en, indien van toepassing, douanerechten in uw land. Als AliExpress wettel ij k verplicht is om btw te innen, ziet u de pr ij s inclusief btw b ij het afrekenen. Neem voor meer informatie over deze kosten contact op met de belastingdienst en douaneautoriteiten in uw land. Verkocht door Tools-Electronics Store, verwerkt door Tools-Electronics Store Informatie over productconformiteit Disclaimer Disclaimer: Afbeeldingen en productinformatie op deze pagina kunnen automatisch door AI z ij n vertaald. Deze vertalingen worden uitsluitend ter gemak aangeboden en worden geleverd zoals ze z ij n, en kunnen onnauwkeurigheden of inconsistenties bevatten. Tools-Electronics Store 95.9% Positieve feedback | 14K Volgers Aanbevolen uit Tools-Electronics Store Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Dit vindt u misschien ook leuk VRX-75DF 7,2 GH … € 87 , 99 €84,54 bespaard 3 × €29,33 zonder rente Android Mobiele Telefoon The … € 77 , 69 €143,87 -45% 1 3 × €25,89 zonder rente RX8500-SPI FPV Video-ontva … € 74 , 39 €137,76 -46% 3 × €24,79 zonder rente RX8200 6G-8.2G … € 66 , 99 €69,72 bespaard 3 × €22,33 zonder rente RX7264 FPV-zende € 72 , 99 €137,72 - 3 × €24,33 zonder re Elektrisch Gereedschap … € 4 , 99 €9,94 -49% 4.9 500+ verkocht €2 korting op €15 Gratis levering vanaf €10 Mini Touch Sensor Dimbare Sc … € 1 , 62 €3,12 -48% 5 69 verkocht €2 korting op €15 DC3V-30V Touch Key S … € 2 , 84 5 42 verkocht Gratis levering vanaf €10 12V Capacitieve Aanraa … € 3 , 24 4.8 128 verkocht Gratis levering vanaf €10 Beste pr ij s voor vergel ij kbare 1 € 1 , 99 4.9 Invoerrechten verrek €2,28 bespaard Bundeldeals Universele elektrocardiograaf- … € 1 , 69 5 399 verkocht Dual Button Touch Mod … € 2 , 9 €4,23 -31% 5 28 verkocht €2 korting op €15 Elk €2,76, ≥ 2 stuks DC3V-30V Touch … € 0 , 82 5 600+ verkocht €2 korting op €15 €0,82 bespaard Hi-Link HLK-LD24 … € 0 , 64 5 107 verkocht €2 korting op €15 €0,02 bespaard Contactloos Capacit € 5 , 59 €7,87 -28% 5 104 ver 5 stks Piëzo-elektrische … € 1 , 71 ( €0,34 / stuk ) 4.6 150 verkocht Gratis levering vanaf €10 20/50/100 STKS/PART … € 2 , 35 4.9 1.000+ verkocht Bestverkopend op AliExpress Gratis levering vanaf €10 3/4 Draad Capacitieve … € 3 , 03 5 70 verkocht Gratis levering vanaf €10 30 stks TCRT5000 L ij n Track S … € 9 , 89 1 verkocht A € 3 , 69 €8,02 -53% 4.8 1.000 €2 korting op €15 Elk €3,5, ≥ 2 stuks ad. ad. ad. ad. Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje
Help Helpcentrum , Geschillen en meldingen , Retour-en Restitutiebeleid , Melden overtredingen van intellectuele-eigendomsrechten , DSA/OSA-informatie , Integriteitsnal Klachteningang zonder inloggen , Retourbeleid Meertalige AliExpress-sites Pусский , Portuguese , Spanish , French , German , Italian , Dutch , Turkish , Japanese , Korean , Thai , Arabic , Hebrew , Polish Browse by Category Alles populaire , Product , Promotie , Lage pr ij s , Uitstekende Waarde , Beoordelingen , Wiki , Video Alibaba Group Alibaba Group Website , AliExpress , Alimama , Fliggy , Alibaba Cloud , Alibaba International , AliTelecom , DingTalk , Juhuasuan , Taobao Marketplace , Tmall , Taobao Global , AliOS , 1688 Mensen zochten ook naar Dit product behoort tot Home Intellectual Property Protection - Privacy Policy - Cookievoorkeuren - Overzicht - Terms of Use - Informatie voor EU-Consumenten - Overeenkomst voor transac Algemene Voorwaarden voor Consumenten in de EU/EEA/UK - User Information Legal Enquiry Guide © 2010-2025 AliExpress.com. All rights reserved. Quectel Lc29Haa Da Ea Ba Bs … € 14 , 79 4.9 136 verkocht Touch Key Switch Module Aan … € 0 , 69 €0,78 -12% 5 17 verkocht Aanpasbaar Aanraakschakelaar Cap … € 1 , 72 4.8 313 verkocht Gratis levering vanaf €10 Beste pr ij s voor vergel ij kbare Moderne beukenhoute … € 1 , 91 ( €0,64 / stuk ) 4.8 900+ verkocht Gratis levering vanaf €10 € 1 , 28 ( €0,46 / stuk 4.9 5.000 €2 korting op €15 Elk €1,22, ≥ 3 stuks ad. ad. Download de AliExpress- app Hi, Eddy Account NL / EUR … Winkelwagentje

Binary file not shown.

View file

@ -4,6 +4,7 @@ package com.eddy.atomrelay
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
@ -30,8 +31,8 @@ class MainActivity : AppCompatActivity(), BleCallbacks {
private lateinit var relay1Button: Button private lateinit var relay1Button: Button
private lateinit var relay2Button: Button private lateinit var relay2Button: Button
private var lastTheme: String? = null private var lastTheme: String? = null
private var originalTint1: ColorStateList? = null private var originalBg1: Drawable? = null
private var originalTint2: ColorStateList? = null private var originalBg2: Drawable? = null
override fun onStatusChanged(status: BleStatus) { override fun onStatusChanged(status: BleStatus) {
when (status) { when (status) {
@ -149,7 +150,7 @@ class MainActivity : AppCompatActivity(), BleCallbacks {
private fun setupRelayButton() { private fun setupRelayButton() {
relay1Button.setOnClickListener(null) relay1Button.setOnClickListener(null)
loadButtonLabel(relay1Button, PrefsHelper.PREF_LABEL1, R.string.button_hold) loadButtonLabel(relay1Button, PrefsHelper.PREF_LABEL1, R.string.button_hold)
originalTint1 = relay1Button.backgroundTintList originalBg1 = relay1Button.background.constantState?.newDrawable()
val accentColor = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.colorAccent)) val accentColor = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.colorAccent))
relay1Button.setOnTouchListener { _, event -> relay1Button.setOnTouchListener { _, event ->
@ -161,7 +162,8 @@ class MainActivity : AppCompatActivity(), BleCallbacks {
} }
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
manager.sendRelayCommand(0x00.toByte()) manager.sendRelayCommand(0x00.toByte())
relay1Button.backgroundTintList = originalTint1 relay1Button.backgroundTintList = null
originalBg1?.let { relay1Button.background = it }
true true
} }
else -> false else -> false
@ -172,7 +174,7 @@ class MainActivity : AppCompatActivity(), BleCallbacks {
private fun setupRelay2Button() { private fun setupRelay2Button() {
relay2Button.setOnClickListener(null) relay2Button.setOnClickListener(null)
loadButtonLabel(relay2Button, PrefsHelper.PREF_LABEL2, R.string.button_hold) loadButtonLabel(relay2Button, PrefsHelper.PREF_LABEL2, R.string.button_hold)
originalTint2 = relay2Button.backgroundTintList originalBg2 = relay2Button.background.constantState?.newDrawable()
val accentColor = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.colorAccent)) val accentColor = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.colorAccent))
relay2Button.setOnTouchListener { _, event -> relay2Button.setOnTouchListener { _, event ->
@ -184,7 +186,8 @@ class MainActivity : AppCompatActivity(), BleCallbacks {
} }
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
manager.sendRelayCommand(0x03.toByte()) manager.sendRelayCommand(0x03.toByte())
relay2Button.backgroundTintList = originalTint2 relay2Button.backgroundTintList = null
originalBg2?.let { relay2Button.background = it }
true true
} }
else -> false else -> false

View file

@ -0,0 +1,192 @@
# ESP32-PICO-D4 (M5Stack Atom) Port Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Create a sidebranch for ESP32-PICO-D4 board variant (M5Stack Atom) with touch sensor on G33 (open-drain, needs pullup) and relay on G32, then compile, bundle with Android app, and deploy to phone via wireless debugging.
**Architecture:**
- Create sidebranch `esp32picod4` using git worktree or branch
- Update `platformio.ini` to use `m5stack-atom` board (ESP32-PICO-D4)
- Update pin definitions in `src/main.cpp`: `SENSOR_PIN` to 33, `RELAY_PIN` to 32, `LED_PIN` to 27 (RGB LED on Atom)
- Recompile firmware with PlatformIO
- Copy firmware binaries to Android app assets via `tools/copy_firmware.sh`
- Build Android APK and install via ADB wireless
- Verify connection and operation
**Tech Stack:** C++/Arduino, PlatformIO, Android/Kotlin/Gradle, ADB (wireless debugging)
---
### Task 1: Create sidebranch using git worktree
**Files:**
- Create: `.worktrees/esp32picod4` (git worktree directory)
- [ ] **Step 1: Create and switch to sidebranch in worktree**
```bash
git worktree add -b esp32picod4 .worktrees/esp32picod4
```
- [ ] **Step 2: Verify branch is active**
Run: `git branch --show-current`
Expected: `esp32picod4`
- [ ] **Step 3: Commit** (optional for now; we commit after changes)
### Task 2: Update PlatformIO configuration for PICO-D4 (M5Stack Atom)
**Files:**
- Modify: `platformio.ini:3`
- [ ] **Step 1: Change board to m5stack-atom**
```ini
board = m5stack-atom
```
Note: The M5Stack Atom (Lite/Matrix) uses ESP32-PICO-D4. The board ID in PlatformIO is `m5stack-atom`.
- [ ] **Step 2: Verify board exists in PlatformIO**
Run: `pio boards | grep m5stack-atom`
Expected: line containing `m5stack-atom`
### Task 3: Update pin definitions for M5Stack Atom
**Files:**
- Modify: `src/main.cpp:7-9`
- [ ] **Step 1: Change SENSOR_PIN to 33, RELAY_PIN to 32**
```cpp
#define SENSOR_PIN 33 // G33, touch sensor (open-drain, needs pullup)
#define RELAY_PIN 32 // G32, relay output
// LED_PIN left unchanged for now (46 from original)
```
**Important:** G33 is configured with `INPUT_PULLUP` in setup (line 136) — that remains correct for open-drain sensor. No change needed there.
### Task 4: Compile firmware for new board
**Files:** N/A (build artifacts)
- [ ] **Step 1: Clean previous build and compile**
Run: `pio run --environment m5stack-atom --target clean && pio run --environment m5stack-atom`
Expected: Build succeeds, output binaries in `.pio/build/m5stack-atom/`
- [ ] **Step 2: Verify build output files exist**
Run: `ls .pio/build/m5stack-atom/firmware*.bin .pio/build/m5stack-atom/bootloader*.bin .pio/build/m5stack-atom/partitions*.bin .pio/build/m5stack-atom/boot_app0*.bin`
Expected: 4 files: `firmware.bin`, `bootloader.bin`, `partitions.bin`, `boot_app0.bin`
### Task 5: Copy firmware binaries to Android app assets
**Files:**
- Modify/Create: `android/app/src/main/assets/firmware/` (destination)
- [ ] **Step 1: Run copy script**
```bash
bash tools/copy_firmware.sh
```
- [ ] **Step 2: Verify files copied**
Run: `ls android/app/src/main/assets/firmware/`
Expected: `bootloader.bin`, `partitions.bin`, `firmware.bin`, `boot_app0.bin`
- [ ] **Step 3: Commit changes**
```bash
git add platformio.ini src/main.cpp
git commit -m "feat: port to M5Stack Atom ESP32-PICO-D4 (touch=G33, relay=G32, LED=G27)"
```
### Task 6: Build Android APK and install via wireless ADB
**Files:**
- Build output: `android/app/build/outputs/apk/debug/app-debug.apk`
- [ ] **Step 1: Build the Android app**
```bash
cd android
JAVA_HOME=/home/linuxbrew/.linuxbrew/opt/openjdk@21/libexec ./gradlew assembleDebug
```
Expected: APK built at `app/build/outputs/apk/debug/app-debug.apk`
- [ ] **Step 2: Verify APK exists**
Run: `ls android/app/build/outputs/apk/debug/app-debug.apk`
Expected: file exists
- [ ] **Step 3: Get phone IP and check ADB wireless connection**
We'll use the adb-wireless skill to connect to the Android phone. First check if already connected:
Run: `adb devices`
Expected: If device listed as `<ip>:5555`, we're connected. If not, we need to connect.
- [ ] **Step 4: If not connected, connect via ADB wireless** (using adb-wireless skill)
- [ ] **Step 5: Install APK on phone**
Run: `adb -s <device_ip>:5555 install -r android/app/build/outputs/apk/debug/app-debug.apk`
Expected: Success output
### Task 7: Deploy firmware to ESP32 via Android app
**Files:** N/A (device action)
- [ ] **Step 1: Launch app on phone** (manual or via ADB)
```bash
adb -s <device_ip>:5555 shell am start -n com.eddy.atomrelay/.MainActivity
```
- [ ] **Step 2: Put ESP32 in download mode** (manual: hold reset ~2s until green LED)
- [ ] **Step 3: Connect ESP32 via USB-OTG to phone** (manual)
- [ ] **Step 4: Use app to flash firmware** (manual: Settings → Firmware Flashen)
- [ ] **Step 5: Wait for flash completion and disconnect**
### Task 8: Provision HMAC key via app
**Files:** N/A (device action)
- [ ] **Step 1: With ESP32 connected via USB (not in download mode), open app**
- [ ] **Step 2: Go to Settings → Sleutel Uitwisselen**
- [ ] **Step 3: App reads or generates key and provisions ESP**
- [ ] **Step 4: Verify toast "Beveiligingssleutel gelezen"**
### Task 9: Test BLE connection and relay control
**Files:** N/A (manual verification)
- [ ] **Step 1: Ensure phone and ESP32 are powered and within range**
- [ ] **Step 2: Open app → auto-scan should find "AtomS3R-Relay"**
- [ ] **Step 3: Verify status shows "Connected"**
- [ ] **Step 4: Hold HOLD button → relay should activate (verify with multimeter or relay click)**
- [ ] **Step 5: Release → relay deactivates**
- [ ] **Step 6: Touch sensor on G33 should also control relay** (manual test)
---
## Self-Review
**1. Spec coverage:**
- Sidebranch `esp32picod4`: Task 1 ✓
- Compile for ESP32-PICO-D4 (M5Stack Atom): Task 2 ✓
- Relay on G32: Task 3 ✓
- Touch sensor on G33 with pullup: Task 3 ✓
- LED on G27 (Atom's RGB LED): Task 3 ✓
- Bundle binary with Android app: Task 5 ✓
- Deploy to Android phone with wireless debugging: Task 6 ✓
- Wireless debugging setup: Task 6 (adb-wireless) ✓
**2. Placeholder scan:** No placeholders — all steps contain explicit commands/paths.
**3. Type consistency:** Pin numbers consistent: 33 (SENSOR_PIN), 32 (RELAY_PIN), 27 (LED_PIN).

View file

@ -0,0 +1,499 @@
# ESP32-PICO-D4 (M5Stack Atom Lite) Port — Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Port the Sylvia Touch Relay project from ESP32-S3-Pico to ESP32-PICO-D4 (Atom Lite) with relay on G32, touch on G33 (pullup), SK6812 LED on G27, CDC-class USB detection in the Android app, and deploy via ADB wireless.
**Architecture:** Create side branch `esp32picod4` via git worktree. Replace platformio.ini S3 config with `m5stack-atom` board + FastLED library. Update pin definitions and LED code in main.cpp. Replace Android USB VID filter with CDC-ACM class filter. No DTR/RTS changes needed — the esp32-flash-lib handles it correctly.
**Tech Stack:** C++/Arduino (FastLED), PlatformIO (espressif32@6.7.0), Android/Kotlin (AGP 8.5.2, Gradle 8.14), ADB (wireless debugging)
---
### Task 1: Create sidebranch using git worktree
**Files:**
- Create: `.worktrees/esp32picod4/` (git worktree directory)
- [ ] **Step 1: Create and switch to sidebranch in worktree**
```bash
git worktree add -b esp32picod4 .worktrees/esp32picod4
```
- [ ] **Step 2: Verify branch is active**
```bash
git -C .worktrees/esp32picod4 branch --show-current
```
Expected: `esp32picod4`
---
### Task 2: Update PlatformIO configuration for PICO-D4
**Files:**
- Modify: `platformio.ini`
**Worktree path:** `.worktrees/esp32picod4/platformio.ini`
- [ ] **Step 1: Replace entire platformio.ini content**
```ini
[env:m5stack-atom]
platform = espressif32@6.7.0
board = m5stack-atom
framework = arduino
build_flags =
-DESP32
-DCORE_DEBUG_LEVEL=5
lib_deps =
fastled/FastLED@^3.7.0
upload_port = /dev/ttyUSB0
upload_speed = 115200
```
- [ ] **Step 2: Verify m5stack-atom board exists in PlatformIO**
```bash
pio boards --json-output 2>/dev/null | python3 -c "import json,sys; boards=[b['id'] for b in json.load(sys.stdin) if 'm5stack-atom' in b['id']]; print('\n'.join(boards) if boards else 'NOT FOUND')"
```
Expected: `m5stack-atom` (or similar variant)
- [ ] **Step 3: Commit platformio.ini**
```bash
git -C .worktrees/esp32picod4 add platformio.ini
git -C .worktrees/esp32picod4 commit -m "feat: switch platformio config to m5stack-atom (ESP32-PICO-D4)"
```
---
### Task 3: Update firmware for Atom Lite (pins, LED, BLE name)
**Files:**
- Modify: `src/main.cpp`
**Worktree path:** `.worktrees/esp32picod4/src/main.cpp`
- [ ] **Step 1: Replace pin definitions (lines 10-12)**
Replace:
```cpp
#define SENSOR_PIN 38
#define RELAY_PIN 1
#define LED_PIN 46
```
With:
```cpp
#include <FastLED.h>
#define SENSOR_PIN 33
#define RELAY_PIN 32
#define LED_PIN 27
#define NUM_LEDS 1
CRGB leds[NUM_LEDS];
```
- [ ] **Step 2: Replace setup() LED init and blink code (lines 139-149)**
Replace:
```cpp
pinMode(SENSOR_PIN, INPUT_PULLUP);
pinMode(RELAY_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
for (int i = 0; i < 3; i++) {
digitalWrite(LED_PIN, HIGH);
delay(200);
digitalWrite(LED_PIN, LOW);
delay(200);
}
Serial.println("LED blink done, starting BLE...");
```
With:
```cpp
pinMode(SENSOR_PIN, INPUT_PULLUP);
pinMode(RELAY_PIN, OUTPUT);
FastLED.addLeds<SK6812, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(32);
for (int i = 0; i < 3; i++) {
leds[0] = CRGB::Green;
FastLED.show();
delay(200);
leds[0] = CRGB::Black;
FastLED.show();
delay(200);
}
Serial.println("LED blink done, starting BLE...");
```
- [ ] **Step 3: Replace Serial banner and BLE device name**
Replace (line 125):
```cpp
Serial.println("=== Atom VoiceS3R BLE Relay ===");
```
With:
```cpp
Serial.println("=== Atom Lite BLE Relay ===");
```
Replace (line 151):
```cpp
BLEDevice::init("AtomS3R-Relay");
```
With:
```cpp
BLEDevice::init("SyldaServer");
```
Replace (line 174):
```cpp
Serial.println("BLE: advertising started as 'AtomS3R-Relay'");
```
With:
```cpp
Serial.println("BLE: advertising started as 'SyldaServer'");
```
- [ ] **Step 4: Verify main.cpp has no remaining S3-specific or AtomS3R references**
```bash
grep -n "S3R\|S3\|Atom Voice\|46\|G38\|G1\b" .worktrees/esp32picod4/src/main.cpp
```
Expected: no output (no matches)
- [ ] **Step 5: Commit main.cpp**
```bash
git -C .worktrees/esp32picod4 add src/main.cpp
git -C .worktrees/esp32picod4 commit -m "feat: port firmware to Atom Lite (G32 relais, G33 touch, G27 SK6812 LED, SyldaServer BLE)"
```
---
### Task 4: Update copy_firmware.sh for new environment name
**Files:**
- Modify: `tools/copy_firmware.sh`
**Worktree path:** `.worktrees/esp32picod4/tools/copy_firmware.sh`
- [ ] **Step 1: Change environment directory name (line 4)**
Replace:
```bash
BINARY_DIR="$(cd "$(dirname "$0")/.." && pwd)/.pio/build/m5stack-atoms3r"
```
With:
```bash
BINARY_DIR="$(cd "$(dirname "$0")/.." && pwd)/.pio/build/m5stack-atom"
```
- [ ] **Step 2: Commit**
```bash
git -C .worktrees/esp32picod4 add tools/copy_firmware.sh
git -C .worktrees/esp32picod4 commit -m "chore: update copy_firmware.sh for m5stack-atom environment"
```
---
### Task 5: Add CDC-class USB detection to UsbUtils.kt
**Files:**
- Modify: `android/app/src/main/java/com/eddy/atomrelay/UsbUtils.kt`
**Worktree path:** `.worktrees/esp32picod4/android/app/src/main/java/com/eddy/atomrelay/UsbUtils.kt`
- [ ] **Step 1: Add hasCdcDataInterface function**
After the `findBulkEndpoint` function (before the closing `}` of `object UsbUtils`), add:
```kotlin
fun hasCdcDataInterface(device: UsbDevice): Boolean {
for (i in 0 until device.interfaceCount) {
if (device.getInterface(i).interfaceClass == CDC_DATA_INTERFACE_CLASS) {
return true
}
}
return false
}
```
- [ ] **Step 2: Commit**
```bash
git -C .worktrees/esp32picod4 add android/app/src/main/java/com/eddy/atomrelay/UsbUtils.kt
git -C .worktrees/esp32picod4 commit -m "feat: add CDC-class USB detection for universal bridge support"
```
---
### Task 6: Replace VID filter with CDC-class filter in SettingsActivity.kt
**Files:**
- Modify: `android/app/src/main/java/com/eddy/atomrelay/SettingsActivity.kt`
**Worktree path:** `.worktrees/esp32picod4/android/app/src/main/java/com/eddy/atomrelay/SettingsActivity.kt`
- [ ] **Step 1: Remove ESP_VENDOR_ID constant (line 39)**
Delete:
```kotlin
private const val ESP_VENDOR_ID = 0x303a
```
- [ ] **Step 2: Replace VID check in usbAttachedReceiver (line 67)**
Replace:
```kotlin
if (device?.vendorId == ESP_VENDOR_ID) {
```
With:
```kotlin
if (device != null && UsbUtils.hasCdcDataInterface(device)) {
```
- [ ] **Step 3: Replace VID check in usbPermissionReceiver (line 85)**
Replace:
```kotlin
val device = usbManager.deviceList.values.firstOrNull { it.vendorId == ESP_VENDOR_ID }
```
With:
```kotlin
val device = usbManager.deviceList.values.firstOrNull { UsbUtils.hasCdcDataInterface(it) }
```
- [ ] **Step 4: Replace VID check in usbDetachedReceiver (line 101)**
Replace:
```kotlin
if (device?.vendorId == ESP_VENDOR_ID) {
```
With:
```kotlin
if (device != null && UsbUtils.hasCdcDataInterface(device)) {
```
- [ ] **Step 5: Replace VID check in startKeyExchange (line 208)**
Replace:
```kotlin
val device = usbManager.deviceList.values.firstOrNull { it.vendorId == ESP_VENDOR_ID }
```
With:
```kotlin
val device = usbManager.deviceList.values.firstOrNull { UsbUtils.hasCdcDataInterface(it) }
```
- [ ] **Step 6: Replace VID check in startFlashing (line 256)**
Replace:
```kotlin
val device = usbManager.deviceList.values.firstOrNull { it.vendorId == ESP_VENDOR_ID }
```
With:
```kotlin
val device = usbManager.deviceList.values.firstOrNull { UsbUtils.hasCdcDataInterface(it) }
```
- [ ] **Step 7: Verify no remaining ESP_VENDOR_ID references**
```bash
grep -n "ESP_VENDOR_ID\|vendorId.*==" .worktrees/esp32picod4/android/app/src/main/java/com/eddy/atomrelay/SettingsActivity.kt
```
Expected: no output
- [ ] **Step 8: Commit**
```bash
git -C .worktrees/esp32picod4 add android/app/src/main/java/com/eddy/atomrelay/SettingsActivity.kt
git -C .worktrees/esp32picod4 commit -m "feat: replace USB VID filter with CDC-ACM class detection"
```
---
### Task 7: Compile firmware
**Files:** N/A (build artifacts in `.pio/build/m5stack-atom/`)
**Work tree path:** `.worktrees/esp32picod4/`
- [ ] **Step 1: Clean and compile**
```bash
pio run -d .worktrees/esp32picod4 -e m5stack-atom -t clean
pio run -d .worktrees/esp32picod4 -e m5stack-atom
```
Expected: `SUCCESS` at end of output
- [ ] **Step 2: Verify build output files**
```bash
ls -la .worktrees/esp32picod4/.pio/build/m5stack-atom/firmware.bin \
.worktrees/esp32picod4/.pio/build/m5stack-atom/bootloader.bin \
.worktrees/esp32picod4/.pio/build/m5stack-atom/partitions.bin
```
Expected: all 3 files exist with non-zero size
---
### Task 8: Copy firmware binaries to Android app assets
**Files:**
- Modify: `android/app/src/main/assets/firmware/` (destination)
**Worktree path:** `.worktrees/esp32picod4/`
- [ ] **Step 1: Run copy script**
```bash
bash .worktrees/esp32picod4/tools/copy_firmware.sh
```
Expected: lists 4 files (bootloader.bin, partitions.bin, firmware.bin, boot_app0.bin)
- [ ] **Step 2: Verify boot_app0.bin is also present**
```bash
ls -la .worktrees/esp32picod4/android/app/src/main/assets/firmware/boot_app0.bin
```
Expected: file exists
- [ ] **Step 3: Commit binary assets**
```bash
git -C .worktrees/esp32picod4 add android/app/src/main/assets/firmware/
git -C .worktrees/esp32picod4 commit -m "feat: add Atom Lite firmware binaries to Android assets"
```
---
### Task 9: Build Android APK
**Files:** N/A (build output `android/app/build/outputs/apk/debug/app-debug.apk`)
**Worktree path:** `.worktrees/esp32picod4/android/`
- [ ] **Step 1: Build debug APK**
```bash
JAVA_HOME=/home/linuxbrew/.linuxbrew/opt/openjdk@21/libexec ./gradlew assembleDebug
```
Expected: `BUILD SUCCESSFUL`
- [ ] **Step 2: Verify APK exists**
```bash
ls -la app/build/outputs/apk/debug/app-debug.apk
```
Expected: file exists with size > 1MB
---
### Task 10: Connect Android phone via ADB wireless
**Files:** N/A (ADB system tool)
- [ ] **Step 1: Load adb-wireless skill**
Use the `adb-wireless` skill to establish a wireless ADB connection to the Android phone.
- [ ] **Step 2: Verify ADB connection**
```bash
adb devices
```
Expected: `<ip>:5555` listed as `device`
---
### Task 11: Install APK on phone
**Files:** N/A (app deployment)
- [ ] **Step 1: Install APK**
```bash
adb install -r .worktrees/esp32picod4/android/app/build/outputs/apk/debug/app-debug.apk
```
Expected: `Success`
- [ ] **Step 2: Launch the app**
```bash
adb shell am start -n com.eddy.atomrelay/.MainActivity
```
---
### Task 12: Flash firmware to Atom Lite via Android app
**Files:** N/A (manual device action)
- [ ] **Step 1: Connect Atom Lite via USB-OTG to the phone**
- [ ] **Step 2: Open the app, tap settings gear → "Firmware Flashen"**
- [ ] **Step 3: If app doesn't auto-detect, unplug/replug the Atom Lite while the dialog is open**
- [ ] **Step 4: Wait for flash to complete (progress bar reaches 100%)**
- [ ] **Step 5: After flash, disconnect USB — the Atom Lite should reboot with green LED blink 3x**
---
### Task 13: Provision HMAC key via app
**Files:** N/A (manual device action)
- [ ] **Step 1: Reconnect Atom Lite via USB-OTG to phone (NOT in download mode — just power on normally)**
- [ ] **Step 2: Open app → Settings → "Sleutel Uitwisselen"**
- [ ] **Step 3: Wait for toast "Beveiligingssleutel gelezen" or provisioning**
- [ ] **Step 4: If key was newly generated, verify Serial monitor shows "HMAC key stored in NVS"**
---
### Task 14: Verify BLE connection and relay control
**Files:** N/A (manual verification)
- [ ] **Step 1: Power on the Atom Lite (with relay and touch sensor connected)**
- [ ] **Step 2: Open the Android app — verify it auto-connects to "SyldaServer"**
- [ ] **Step 3: Verify status shows "Connected"**
- [ ] **Step 4: Hold HOLD button — verify relay activates (click or multimeter)**
- [ ] **Step 5: Release — verify relay deactivates**
- [ ] **Step 6: Touch sensor on G33 — verify relay also activates**
---
## Self-Review
**1. Spec coverage:**
- Sidebranch `esp32picod4`: Task 1 ✓
- Compile for ESP32-PICO-D4: Task 2 ✓
- Pins G32/G33/G27: Task 3 ✓
- SK6812 LED with FastLED: Task 3 ✓
- BLE name "SyldaServer": Task 3 ✓
- CDC-class USB detection: Tasks 5, 6 ✓
- DTR/RTS no changes: Confirmed in research, no task needed ✓
- Bundle binary with Android app: Task 8 ✓
- Build APK: Task 9 ✓
- ADB wireless deploy: Tasks 10, 11 ✓
- Flash + provision + test: Tasks 12-14 ✓
**2. Placeholder scan:** No TBD, TODO, or incomplete sections. All commands are concrete with expected output.
**3. Type consistency:** `hasCdcDataInterface(device: UsbDevice): Boolean` defined in Task 5, used consistently in Task 6 with `UsbUtils.hasCdcDataInterface(device/it)`.

View file

@ -0,0 +1,127 @@
# ESP32-PICO-D4 (M5Stack Atom Lite) Port — Design Spec
> **Goal:** Port the Sylvia Touch Relay project from ESP32-S3-Pico (Atom VoiceS3R) to ESP32-PICO-D4 (Atom Lite), update the Android app for CDC-class USB detection, compile, bundle, and deploy via ADB wireless.
## Current State
The project targets the M5Stack Atom VoiceS3R with ESP32-S3-PICO-1-N8R8 (8MB Flash, 8MB PSRAM). The Android app filters USB devices by Espressif vendor ID (`0x303A`) for key exchange and firmware flashing. The `esp32-flash-lib` handles DTR/RTS via the `SerialTransport` interface implemented by `UsbSerialTransport`.
## Target Hardware
M5Stack Atom Lite with ESP32-PICO-D4:
- 4MB Flash (no PSRAM)
- FTDI FT2232C/D USB-to-UART bridge (VID `0x0403`) — appears as `/dev/ttyUSB0`
- SK6812 addressable RGB LED on GPIO27
- Relay on GPIO32 (G32)
- Touch sensor on GPIO33 (G33) — open-drain type, needs pullup
## Design Decisions
### 1. Side Branch & Git Worktree
Create branch `esp32picod4` via git worktree at `.worktrees/esp32picod4` to isolate the port from the main S3 variant.
### 2. Firmware (`platformio.ini` + `src/main.cpp`)
**New PlatformIO environment `m5stack-atom`:**
| Setting | Old (S3) | New (PICO-D4) | Reason |
|---------|----------|---------------|--------|
| `board` | `esp32-s3-devkitc-1` | `m5stack-atom` | PlatformIO board ID for M5Stack Atom Lite |
| `board_build.arduino.memory_type` | `qio_opi` | removed | Not needed for PICO-D4 |
| `-DESP32S3` | present | `-DESP32` | Correct chip family |
| `-DBOARD_HAS_PSRAM` | present | removed | Atom Lite has no PSRAM |
| `-mfix-esp32-psram-cache-issue` | present | removed | Only needed with PSRAM |
| `-DARDUINO_USB_CDC_ON_BOOT=1` | present | removed | No native USB on PICO-D4 |
| `-DARDUINO_USB_MODE=1` | present | removed | No native USB |
| `upload_port` | `/dev/ttyACM0` | `/dev/ttyUSB0` | FTDI serial device |
| `lib_deps` | (empty) | `fastled/FastLED@^3.7` | SK6812 Neopixel library |
**Pin definitions:**
| Constant | Old | New | Notes |
|----------|-----|-----|-------|
| `SENSOR_PIN` | 38 | 33 | G33, open-drain, `INPUT_PULLUP` unchanged |
| `RELAY_PIN` | 1 | 32 | G32 |
| `LED_PIN` | 27 | 27 | SK6812 Neopixel — replaces `digitalWrite` with FastLED API |
**BLE device name:** `"SyldaServer"` (was `"AtomS3R-Relay"`)
**Serial banner:** `"Atom Lite BLE Relay"` (was `"Atom VoiceS3R BLE Relay"`)
**LED code change:** Replace `digitalWrite(LED_PIN, HIGH/LOW)` with FastLED:
- Include `FastLED.h`
- Define `#define NUM_LEDS 1`
- `CRGB leds[NUM_LEDS]`
- `FastLED.addLeds<SK6812, LED_PIN, GRB>(leds, NUM_LEDS)` in setup
- Startup blink: 3x green (`leds[0] = CRGB::Green; FastLED.show()`)
No other firmware logic changes — HMAC key exchange, BLE relay control, touch sensor logic remain identical.
### 3. Android USB Detection
**Problem:** The Atom Lite's FTDI bridge presents VID `0x0403`, not Espressif's `0x303A`. The app currently filters on VID and won't detect the device.
**Solution:** Replace VID (`0x303A`) filtering with CDC-ACM class filtering (interface class `0x0A`).
**Changes in `UsbUtils.kt`:**
- Add `hasCdcDataInterface(device: UsbDevice): Boolean` — iterates interfaces, returns true if any has `interfaceClass == 0x0A`
**Changes in `SettingsActivity.kt`:**
- Remove `ESP_VENDOR_ID` constant
- Replace all `device?.vendorId == ESP_VENDOR_ID` checks with `UsbUtils.hasCdcDataInterface(device)`
This detects all CDC-ACM serial devices including:
- FTDI FT2232C/D (Atom Lite)
- CP210x (some Atom Lite revisions)
- ESP32-S3 native USB (current hardware)
- CH340/CH9102 (other Atom variants)
### 4. DTR/RTS — No Changes Needed
**Research findings:**
The `esp32-flash-lib`'s `EspFlasherProtocol.enterBootLoader()` implements the standard DTR/RTS download-mode sequence:
```
setControlLines(true, false) // DTR=High → EN released, RTS=Low → GPIO0=Low (download mode)
delayMS(100)
setControlLines(false, true) // DTR=Low → reset pulse via capacitor, RTS=High → GPIO0=High
delayMS(100)
setControlLines(true, false) // DTR=High → release reset, RTS=Low → GPIO0=Low → boot in download mode
```
The `UsbSerialTransport.setControlLines()` implementation sends `CDC_SET_CONTROL_LINE_STATE` control transfers with correct DTR/RTS bit flags. This works for FTDI, CP2104, and native USB CDC-ACM.
The `SettingsActivity.setupCdcControl()` sets DTR|RTS both HIGH for normal serial operation (key exchange), which is correct for all bridge types.
**Verdict: No changes to `EspFlasher.kt`, `UsbSerialTransport.kt`, or key exchange logic.**
### 5. Build, Bundle & Deploy
1. `pio run -e m5stack-atom` to compile firmware
2. `tools/copy_firmware.sh` to copy binaries to `android/app/src/main/assets/firmware/` (environment dir is now `m5stack-atom`)
3. `./gradlew assembleDebug` in `android/` to build APK
4. ADB wireless to connect phone (`adb-wireless` skill)
5. `adb install -r app-debug.apk` to deploy
## Files Changed
| File | Change |
|------|--------|
| `platformio.ini` | New environment `m5stack-atom` replacing S3 config |
| `src/main.cpp` | New pin definitions, BLE name, FastLED LED code |
| `android/.../UsbUtils.kt` | Add `hasCdcDataInterface()` |
| `android/.../SettingsActivity.kt` | VID filter → CDC class filter |
| `tools/copy_firmware.sh` | Environment name `m5stack-atoms3r` → `m5stack-atom` |
## Files Not Changed
| File | Reason |
|------|--------|
| `EspFlasher.kt` | DTR/RTS handled by library |
| `UsbSerialTransport.kt` | Correct implementation |
| `MainActivity.kt` | No BLE changes beyond device name (auto-discovered) |
| `PrefsHelper.kt` | No changes |
| `AndroidManifest.xml` | No changes |
| `build.gradle` | No dependency changes |