Skip to content

Commit 18fc9f3

Browse files
TianqiYeclaude
andcommitted
Merge PR #1: PlatformIO migration with incoming CLAUDE.md
Merged platformio-migration branch into main, accepting the incoming CLAUDE.md file which contains updated PlatformIO workflow documentation. ## Changes - Updated .gitignore for PlatformIO artifacts - Updated CLAUDE.md with PlatformIO installation and workflow - Removed legacy bin/arduino-cli - Added examples/RotatingDonut/ with PlatformIO structure - Moved BoardConfig from config/ to lib/BoardConfig/ - Added library.json files for shared libraries 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
2 parents 30d2d02 + ff1a2c4 commit 18fc9f3

File tree

12 files changed

+505
-37
lines changed

12 files changed

+505
-37
lines changed

.gitignore

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,36 @@
22
.claude/settings.backup
33
.claude/current_mode
44
.claude/agents/
5+
6+
# PlatformIO
7+
.pio/
8+
.vscode/
9+
.pioenvs/
10+
.piolibdeps/
11+
12+
# Build artifacts
13+
*.bin
14+
*.elf
15+
*.map
16+
17+
# Python
18+
__pycache__/
19+
*.pyc
20+
*.pyo
21+
.Python
22+
23+
# IDE
24+
.idea/
25+
*.swp
26+
*.swo
27+
*~
28+
29+
# OS
30+
.DS_Store
31+
Thumbs.db
32+
33+
# Tool state
34+
.happy/
35+
36+
# Arduino CLI (legacy)
37+
bin/arduino-cli

CLAUDE.md

Lines changed: 157 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
ESP32‑S3 Matrix — Fast Path for New Games + Debug
22

3-
This repo lets anyone build a new LED‑matrix game quickly, visualize it in a terminal, and keep hardware + tools in sync. Follow this flow and youre productive on day one.
3+
This repo lets anyone build a new LED‑matrix game quickly, visualize it in a terminal, and keep hardware + tools in sync. Follow this flow and you're productive on day one.
44

55
1) Configure The Board Once
6-
- Edit `config/BoardConfig.h` (single source of truth):
6+
- Edit `lib/BoardConfig/BoardConfig.h` (single source of truth):
77
- `MATRIX_WIDTH/HEIGHT` (default 8x8)
88
- `LED_PIN` (default 14) and `BRIGHTNESS_LIMIT` (≤ 60)
99
- `COLOR_ORDER` (use `RGB` for Waveshare ESP32‑S3‑Matrix)
1010
- Wiring/orientation: `PANEL_WIRING_SERPENTINE` (0=progressive row‑major), `PANEL_ROTATION`, `PANEL_FLIP_X/Y`
1111
- All games include this file; change it once and everything follows.
1212

1313
2) Use The Shared Helpers
14-
- Include in your sketch:
15-
- `#include "config/BoardConfig.h"`
16-
- `#include "lib/MatrixUtil/MatrixUtil.h"`
14+
- Include in your sketch (`src/main.cpp`):
15+
```cpp
16+
#include <FastLED.h>
17+
#include <BoardConfig.h>
18+
#include <MatrixUtil.h>
19+
```
1720
- What you get:
1821
- `MU_XY(x,y)`: stable XY→index mapping honoring the board profile
1922
- `MU_ADD_LEDS(DATA_PIN, leds, count)`: FastLED init using shared `COLOR_ORDER`
@@ -23,9 +26,10 @@ This repo lets anyone build a new LED‑matrix game quickly, visualize it in a t
2326

2427
Minimal New‑Game Template
2528
```cpp
29+
// src/main.cpp
2630
#include <FastLED.h>
27-
#include "config/BoardConfig.h"
28-
#include "lib/MatrixUtil/MatrixUtil.h"
31+
#include <BoardConfig.h>
32+
#include <MatrixUtil.h>
2933

3034
#define NUM_LEDS (MATRIX_WIDTH * MATRIX_HEIGHT)
3135
CRGB leds[NUM_LEDS];
@@ -53,40 +57,129 @@ void loop() {
5357
}
5458
```
5559

56-
3) Build / Flash (ESP32‑S3) **IMPORTANT MAKE SURE TO FOLLOW**
57-
-USB CDC On Boot = Enabled (prevents Serial from blocking) use and only use if you need to debug Serial.
58-
- Use the bundled CLI at `./bin/arduino-cli`:
59-
- First‑time setup (once):
60-
- `./bin/arduino-cli config init`
61-
- `./bin/arduino-cli core update-index`
62-
- Prepare the dependency for esp32
63-
- `./bin/arduino-cli config set network.connection_timeout 1000s` Increase the timeout for the big download, WARN USER this gonna take some time, possible (10-20min), so need to be patient.
64-
- `GODEBUG=http2client=0 ./bin/arduino-cli --log-level debug core install esp32:esp32` (Force HTTP/1.1 bypasses the flaky HTTP/2 path)
65-
- Wait for the download to finish, dont put it background, do this step linearly.
66-
- Compile (example: Snake):
67-
- `./bin/arduino-cli compile --fqbn esp32:esp32:esp32s3:CDCOnBoot=cdc examples/Snake`
68-
- Upload:
69-
- **IMPORTANT find the correct port by checking lsusb first**
70-
- `./bin/arduino-cli upload --fqbn esp32:esp32:esp32s3:CDCOnBoot=cdc --port /dev/ttyACM0 examples/Snake`
71-
- Monitor:
72-
- `./bin/arduino-cli monitor --port /dev/ttyACM0 --config baudrate=115200`
73-
- NOTE : you should not suggest user to use Arduino IDE to upload, they cli tool given to you should be very suffcient.
74-
75-
4) Visualize In Terminal (Use only when trying to debug)
60+
3) Visualize In Terminal (Use only when trying to debug)
7661
- Auto‑detect port, auto‑configure from META:
7762
- `python3 tools/led_matrix_viz.py --list-ports`
7863
- `python3 tools/led_matrix_viz.py -p /dev/ttyACM? -b 115200 --stats --verbose`
79-
- If you dont print META, pass flags: `--width/--height --input-order xy --wiring progressive --rotate ...`
64+
- If you don't print META, pass flags: `--width/--height --input-order xy --wiring progressive --rotate ...`
8065
- Tips: `--ascii` for plain text, `--flip-x/--flip-y` for quick checks.
8166

67+
4) Build / Flash (ESP32‑S3) **PLATFORMIO**
68+
69+
## Installing PlatformIO (CM5/Raspberry Pi)
70+
71+
**Recommended: Use `uv` (fast & clean)**
72+
```bash
73+
# Install uv (modern Python package manager)
74+
curl -LsSf https://astral.sh/uv/install.sh | sh
75+
source ~/.bashrc # Or restart terminal
76+
77+
# Install PlatformIO
78+
uv tool install platformio
79+
80+
# Verify
81+
pio --version
82+
```
83+
84+
**Alternative: Quick install (works immediately)**
85+
```bash
86+
pip3 install platformio --break-system-packages
87+
```
88+
89+
**Why uv?** 10-100x faster than pip, handles PATH automatically, keeps Python environment clean. It's the modern standard for installing Python CLI tools.
90+
91+
## Create New Game
92+
```bash
93+
# Create project structure
94+
mkdir -p examples/MyGame/src
95+
cd examples/MyGame
96+
```
97+
98+
**Create platformio.ini:**
99+
```ini
100+
[env:waveshare-esp32s3-matrix]
101+
platform = espressif32
102+
board = adafruit_feather_esp32s3 # 4MB flash (matches Waveshare hardware)
103+
framework = arduino
104+
lib_deps =
105+
fastled/FastLED@^3.9.20
106+
lib_extra_dirs = ../../lib # Shared libs at repo root
107+
monitor_speed = 115200
108+
upload_speed = 921600
109+
```
110+
111+
**Create src/main.cpp** - copy template above
112+
113+
## Build & Upload
114+
```bash
115+
# Build (first time downloads ~1.5GB ESP32 toolchain, 5-10 min)
116+
pio run
117+
118+
# Upload (auto-detects port)
119+
pio run -t upload
120+
121+
# Upload to specific port
122+
pio run -t upload --upload-port /dev/ttyACM0
123+
124+
# Clean
125+
pio run -t clean
126+
```
127+
128+
## Upload Troubleshooting
129+
130+
### Normal Case (No Buttons Needed)
131+
```bash
132+
pio run -t upload # Usually just works
133+
```
134+
135+
You should see connecting dots then progress bars:
136+
```
137+
Connecting....
138+
Writing at 0x00010000... (10 %)
139+
Writing at 0x00020000... (20 %)
140+
```
141+
If this happens, you're done! No buttons needed.
142+
143+
### If Upload Fails ("Connecting..." Forever)
144+
145+
If you see endless dots with no progress:
146+
```
147+
Connecting........._____....._____
148+
```
149+
150+
**Manual button sequence:**
151+
1. Hold **BOOT** button
152+
2. While holding BOOT, press **RESET** button once
153+
3. Release both buttons
154+
4. Run `pio run -t upload` again within 10 seconds
155+
156+
**Still failing after button sequence?**
157+
- Check USB cable is plugged in firmly
158+
- Confirm device detected: `lsusb | grep -i esp` should show "Espressif USB JTAG/serial debug unit"
159+
- Try different USB port on your computer
160+
- Check cable quality (some cables are power-only, no data)
161+
162+
### If Code Crashes or Acts Weird
163+
- Press **RESET** button once (no BOOT needed)
164+
- This restarts your code from the beginning
165+
166+
### Quick Reference
167+
168+
| Issue | Solution |
169+
|-------|----------|
170+
| Upload times out with dots | BOOT+RESET sequence → retry upload |
171+
| Code frozen/not responding | Press RESET only |
172+
| "Couldn't find board" | Check `lsusb`, check cable, try button sequence |
173+
| First upload on new board | Often needs manual button sequence |
174+
82175
5) Proven Debug Workflow
83176
- Keep Serial optional: short wait, then guard prints with `if (Serial)`.
84177
- Use `MU_DrawCalibration(leds)` once to prove mapping (TL=G, TR=R, BL=B, BR=W).
85-
- If colors are wrong on hardware, fix `COLOR_ORDER` in `BoardConfig.h` (Waveshare = `RGB`).
178+
- If colors are wrong on hardware, fix `COLOR_ORDER` in `lib/BoardConfig/BoardConfig.h` (Waveshare = `RGB`).
86179
- If left/right swap on alternating rows, set `PANEL_WIRING_SERPENTINE` to `0` (progressive row‑major).
87180

88181
6) Quick Commands
89-
- List ports: `python3 tools/led_matrix_viz.py --list-ports`
182+
- List ports: `python3 tools/led_matrix_viz.py --list-ports` or `lsusb | grep -i esp`
90183
- Visualize: `python3 tools/led_matrix_viz.py -p /dev/ttyACM0 -b 115200 --stats`
91184
- Raw monitor: `python3 tools/monitor_pong.py -p /dev/ttyACM0`
92185
- Demo (no hardware): `python3 tools/led_matrix_viz.py --demo --width 8 --height 8`
@@ -95,14 +188,41 @@ void loop() {
95188
- Start from the template; draw only via `MU_XY()`.
96189
- Never hardcode edges; use `MATRIX_WIDTH/HEIGHT`.
97190
- Limit debug frame rate (≈5–20 FPS) to keep serial stable.
98-
- Update only `BoardConfig.h` for new panels/orientation; all games + tools follow.
191+
- Update only `lib/BoardConfig/BoardConfig.h` for new panels/orientation; all games + tools follow.
192+
- **Shared libs:** BoardConfig and MatrixUtil are at repo root (`lib/`), included as `<BoardConfig.h>` and `<MatrixUtil.h>`
99193

100194
Repo Highlights
101-
- `config/BoardConfig.h` — board profile (geometry, color order, wiring/orientation, brightness)
102-
- `lib/MatrixUtil/MatrixUtil.h` — mapping + serial frame helpers
195+
- `lib/BoardConfig/` — board profile (geometry, color order, wiring/orientation, brightness) **SINGLE SOURCE OF TRUTH**
196+
- `lib/MatrixUtil/` — mapping + serial frame helpers
103197
- `tools/led_matrix_viz.py` — terminal visualizer (reads META to auto‑configure)
104-
- `examples/` — reference sketches (Snake, tilt‑demo, wifi‑slam)
198+
- `examples/RotatingDonut/` — reference PlatformIO project
199+
200+
DEBUGGING ISSUES
201+
- **Flash size mismatch:** If you see "Detected size(4096k) smaller than (8192k)" on boot, use `board = adafruit_feather_esp32s3` in platformio.ini (NOT esp32-s3-devkitc-1)
202+
- **Upload issues:** See "Upload Troubleshooting" section above for button sequences
203+
- **Wrong device detected:** If `lsusb` shows "MicroPython" or "Pico" instead of "Espressif", that's the internal CM5 system. Replug the ESP32's USB cable.
204+
- **Include errors:** Use `#include <BoardConfig.h>` and `#include <MatrixUtil.h>` (angle brackets, not quotes or paths)
205+
- **Shared libs not found:** Add `lib_extra_dirs = ../../lib` to platformio.ini
105206

106-
DEBUGING ISSUES
107-
- when automatic upload fails, always ask for user to manual put device into bootloader mode, then wait for confirm before reflush again
108-
- MicroPython Board in FS mode is a pico device (which belong to the internal system), you should see ESP devie when you try lsusb, if not remind user to try replug in usb
207+
Project Structure
208+
```
209+
esp32-agent-example/
210+
├── lib/ # SHARED LIBS (all games use these)
211+
│ ├── BoardConfig/
212+
│ │ ├── BoardConfig.h # Single source of truth
213+
│ │ └── library.json
214+
│ └── MatrixUtil/
215+
│ ├── MatrixUtil.h
216+
│ └── library.json
217+
├── examples/
218+
│ ├── RotatingDonut/ # Example PlatformIO game
219+
│ │ ├── platformio.ini
220+
│ │ └── src/
221+
│ │ └── main.cpp
222+
│ └── MyGame/ # Your new game
223+
│ ├── platformio.ini # Copy from RotatingDonut
224+
│ └── src/
225+
│ └── main.cpp # Your code here
226+
└── tools/
227+
└── led_matrix_viz.py # Terminal visualizer
228+
```

bin/arduino-cli

-32.8 MB
Binary file not shown.

examples/RotatingDonut/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.pio
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#include <FastLED.h>
2+
#include "../config/BoardConfig.h"
3+
#include "../lib/MatrixUtil/MatrixUtil.h"
4+
5+
#define NUM_LEDS (MATRIX_WIDTH * MATRIX_HEIGHT)
6+
CRGB leds[NUM_LEDS];
7+
8+
// 3D donut parameters
9+
float angleA = 0, angleB = 0;
10+
const float R1 = 0.6; // Smaller tube = bigger hole
11+
const float R2 = 2.2; // Overall donut radius
12+
const float K1 = 6; // Screen distance (zoomed in)
13+
const float K2 = 5; // Viewer distance
14+
15+
// Z-buffer for depth
16+
float zBuffer[NUM_LEDS];
17+
18+
void setup() {
19+
MU_ADD_LEDS(LED_PIN, leds, NUM_LEDS);
20+
FastLED.setBrightness(BRIGHTNESS_LIMIT);
21+
FastLED.clear();
22+
FastLED.show();
23+
}
24+
25+
void renderDonut() {
26+
// Clear screen and z-buffer
27+
FastLED.clear();
28+
for (int i = 0; i < NUM_LEDS; i++) {
29+
zBuffer[i] = 0;
30+
}
31+
32+
// Precompute sines and cosines
33+
float cosA = cos(angleA), sinA = sin(angleA);
34+
float cosB = cos(angleB), sinB = sin(angleB);
35+
36+
// Generate donut points
37+
for (float theta = 0; theta < 2 * PI; theta += 0.3) {
38+
float cosTheta = cos(theta), sinTheta = sin(theta);
39+
40+
for (float phi = 0; phi < 2 * PI; phi += 0.2) {
41+
float cosPhi = cos(phi), sinPhi = sin(phi);
42+
43+
// 3D coordinates of the point on the torus
44+
float circleX = R2 + R1 * cosTheta;
45+
float circleY = R1 * sinTheta;
46+
47+
// Rotate around Y axis (angleB) and X axis (angleA)
48+
float x = circleX * (cosB * cosPhi + sinA * sinB * sinPhi) - circleY * cosA * sinB;
49+
float y = circleX * (sinB * cosPhi - sinA * cosB * sinPhi) + circleY * cosA * cosB;
50+
float z = K2 + cosA * circleX * sinPhi + circleY * sinA;
51+
float ooz = 1 / z; // One over z
52+
53+
// Project to 2D
54+
int xp = (int)(MATRIX_WIDTH / 2 + K1 * ooz * x);
55+
int yp = (int)(MATRIX_HEIGHT / 2 - K1 * ooz * y);
56+
57+
// Check bounds
58+
if (xp >= 0 && xp < MATRIX_WIDTH && yp >= 0 && yp < MATRIX_HEIGHT) {
59+
int pixelIndex = MU_XY(xp, yp);
60+
61+
// Only render if this point is closer than what's already there
62+
if (ooz > zBuffer[pixelIndex]) {
63+
zBuffer[pixelIndex] = ooz;
64+
65+
// Calculate lighting based on surface normal
66+
float L = cosPhi * cosTheta * sinB - cosA * cosTheta * sinPhi - sinA * sinTheta +
67+
cosB * (cosA * sinTheta - cosTheta * sinA * sinPhi);
68+
69+
if (L > 0) {
70+
// Simple grayscale depth - far = dark grey, close = white
71+
// This gives pure depth visualization
72+
73+
// Normalize depth
74+
float depth = (ooz - 0.1) * 7;
75+
depth = max(0.0f, min(1.0f, depth));
76+
77+
// Apply lighting (surface angle affects brightness)
78+
float lit = L * 0.7 + 0.3; // Never fully dark from lighting
79+
80+
// Combine depth and lighting
81+
float brightness = depth * lit;
82+
83+
// Map to grayscale: dark grey (far) to white (close)
84+
// Range from 15 (nearly black) to 255 (white)
85+
uint8_t intensity = 15 + (uint8_t)(brightness * 240);
86+
87+
// Pure grayscale - all RGB channels the same
88+
leds[pixelIndex] = CRGB(intensity, intensity, intensity);
89+
90+
// Optional: Add slight blue tint for style
91+
// leds[pixelIndex] = CRGB(
92+
// intensity * 0.9, // Slightly less red
93+
// intensity * 0.95, // Slightly less green
94+
// intensity // Full blue for cool tint
95+
// );
96+
}
97+
}
98+
}
99+
}
100+
}
101+
}
102+
103+
void loop() {
104+
renderDonut();
105+
FastLED.show();
106+
107+
// Rotate the donut
108+
angleA += 0.07;
109+
angleB += 0.13;
110+
111+
delay(20); // 50 FPS
112+
}

0 commit comments

Comments
 (0)