Skip to content

Commit 7c7379d

Browse files
91voltTaddyHC
andauthored
Docs led matrix painter (#26)
* docs: update bedtime story teller (#20) * Update README example * Revert "docs: update bedtime story teller (#20)" This reverts commit 9491c29. * Update documentation, add thumbnail * Update README * Update README * Optimize thumbnail * Update examples/led-matrix-painter/README.md Co-authored-by: TaddyHC <94547080+TaddyHC@users.noreply.github.com> * Update examples/led-matrix-painter/README.md Co-authored-by: TaddyHC <94547080+TaddyHC@users.noreply.github.com> * Update examples/led-matrix-painter/README.md Co-authored-by: TaddyHC <94547080+TaddyHC@users.noreply.github.com> * Update README AppFrame class description * Update README understanding code section * Fix thumbnail --------- Co-authored-by: TaddyHC <94547080+TaddyHC@users.noreply.github.com>
1 parent 12471c2 commit 7c7379d

File tree

2 files changed

+137
-109
lines changed

2 files changed

+137
-109
lines changed
Lines changed: 137 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
11
# LED Matrix Painter
22

3-
The **LED Matrix Painter** example provides a web-based tool for designing frames and animations for the Arduino UNO Q 8×13 LED matrix. You can create individual frames, organize them into animations, preview them in real-time on the board, and export them as C++ code.
3+
The **LED Matrix Painter** example provides a web-based interface to draw, animate, and control the built-in LED Matrix of the Arduino UNO Q in real-time. It features a pixel editor with 8-bit brightness control, database storage for your designs, and a code generator to export your frames as ready-to-use C++ code.
44

5-
![LED Matrix Painter](assets/docs_assets/led-matrix-painter.png)
5+
![LED Matrix Painter Example](assets/docs_assets/thumbnail.png)
66

77
## Description
88

9-
This example allows you to design LED matrix frames using an interactive web interface. Each frame can have custom brightness levels (0-7) for each LED pixel. You can:
9+
This App allows you to design visuals for the 8x13 LED matrix directly from your browser. It uses the `web_ui` Brick to host a graphical editor where you can paint individual pixels, adjust their brightness, and apply transformations like flipping or rotating. Every change you make in the browser is immediately reflected on the physical board.
1010

11-
- **Design frames** with pixel-by-pixel control using an interactive grid
12-
- **Preview in real-time** on the Arduino UNO Q LED matrix
13-
- **Save and organize** multiple frames with a persistent database
14-
- **Create animations** by sequencing multiple frames together
15-
- **Export code** as C++ arrays ready to use in your Arduino sketches
16-
- **Transform frames** with operations like invert, rotate, and flip
11+
The application uses the `dbstorage_sqlstore` Brick to automatically save your work in a local database. You can create multiple frames, organize them into animations, and use the "Code panel" to see the generated C++ code in real-time.
1712

18-
The application uses the Router Bridge to communicate between the web interface (running on Linux) and the Arduino sketch (running on the microcontroller), enabling real-time updates to the physical LED matrix.
13+
Key features include:
14+
- **Real-time Control:** Drawing on the web grid updates the UNO Q matrix instantly.
15+
- **Grayscale Control:** 8 brightness presets (0-7) for intuitive pixel control, with full 8-bit precision (0-255) supported at the hardware level.
16+
- **Persistent Storage:** Frames are automatically saved to a database, allowing you to build complex animations over time.
17+
- **Transformation Tools:** Invert, rotate, or flip designs with a single click.
18+
- **Animation Mode:** Sequence frames to create animations and preview them on the board.
19+
- **Code Export:** Generate `uint32_t` arrays compatible with the `Arduino_LED_Matrix` library for use in standalone sketches.
1920

2021
## Bricks Used
2122

22-
- `web_ui`: Provides the web server and HTTP API endpoints for the interactive frame designer interface.
23-
- `dbstorage_sqlstore` (implicit): Stores frame data persistently in a SQLite database.
23+
The LED Matrix Painter example uses the following Bricks:
24+
25+
- `web_ui`: Brick to create the interactive grid editor and manage API endpoints.
26+
- `dbstorage_sqlstore`: Brick to persist frames and animation sequences using a SQLite database.
2427

2528
## Hardware and Software Requirements
2629

@@ -35,127 +38,152 @@ The application uses the Router Bridge to communicate between the web interface
3538

3639
## How to Use the Example
3740

38-
1. Launch the App by clicking the **Play** button in the top-right corner. Wait until the App has launched.
39-
2. **Design a frame:**
40-
- Click on individual pixels in the 8×13 grid to toggle them on/off
41-
- Use the brightness slider to adjust LED intensity (0-7)
42-
- Click on pixels with different brightness values to paint with varying intensities
43-
3. **Save your frame:**
44-
- Click the **Save Frame** button to persist the current design
45-
- Frames are automatically saved to a database and appear in the sidebar
46-
4. **Create animations:**
47-
- Save multiple frames
48-
- Switch to **Animations** mode in the sidebar
49-
- Create a new animation and add frames to it
50-
- Use **Play** to preview the animation on the board
51-
5. **Export code:**
52-
- Select the frames or animations you want to export
53-
- Click **Export** to generate C++ code
54-
- Copy the generated arrays into your Arduino sketch
55-
56-
## How it Works
57-
58-
The LED Matrix Painter consists of three main components working together:
59-
60-
- **Web Interface (Frontend)**: An interactive grid editor built with HTML/CSS/JavaScript that sends pixel data to the backend via HTTP API calls.
61-
62-
- **Python Backend**: Handles frame storage in a SQLite database, manages frame transformations, and communicates with the Arduino via Router Bridge.
63-
64-
- **Arduino Sketch**: Receives frame data over Router Bridge and displays it on the physical LED matrix using the `Arduino_LED_Matrix` library.
65-
66-
High-level data flow:
67-
68-
```
69-
Web Browser → HTTP API → Python Backend → Router Bridge → Arduino LED Matrix
70-
71-
SQLite Database
72-
```
73-
74-
The workflow:
75-
1. User edits a frame in the web interface
76-
2. Changes are sent to Python backend via HTTP POST
77-
3. Backend validates and stores the frame in SQLite
78-
4. Backend sends frame data to Arduino via Bridge
79-
5. Arduino sketch renders the frame on the LED matrix
80-
81-
## Understanding the Code
82-
83-
### 🔧 Backend (`main.py`)
84-
85-
The Python application manages the HTTP API, database operations, and communication with the Arduino.
86-
87-
- **`designer = FrameDesigner()`**: Initializes the frame designer utility from `arduino.app_utils`, which provides transformation operations (invert, rotate, flip).
88-
89-
- **`store.init_db()`**: Creates the SQLite database and tables for storing frames if they don't exist.
41+
1. **Run the App**
42+
Launch the example by clicking the **Run** button from Arduino App Lab.
9043

91-
- **`ui.expose_api('POST', '/persist_frame', persist_frame)`**: Exposes an HTTP endpoint that saves or updates frames in the database.
44+
2. **Access the Editor**
45+
Open the App in your browser at `<UNO-Q-IP-ADDRESS>:7000`.
9246

93-
- **`ui.expose_api('POST', '/load_frame', load_frame)`**: Loads a frame from the database by ID or retrieves the last edited frame.
47+
3. **Draw Frames**
48+
- **Paint:** Click any cell in the central grid to turn it on.
49+
- **Adjust Brightness:** Click an active cell again (or hover/wait) to open the floating slider and set the brightness level (0-7).
50+
- **Preview:** Observe the UNO Q; the matrix updates instantly as you draw.
9451

95-
- **`ui.expose_api('GET', '/list_frames', list_frames)`**: Returns all saved frames for display in the sidebar.
52+
4. **Use the Design Tools**
53+
- **Transform:** Use the **Tools** panel on the left to **Flip Vertically/Horizontally**, **Rotate 180°**, **Invert Matrix** (negative), or **Invert Draw** (brightness).
54+
- **Clear:** Use the **Clear Frame** button above the grid to reset the canvas.
9655

97-
- **`ui.expose_api('POST', '/play_animation', play_animation)`**: Sends a sequence of frames to the Arduino to play as an animation.
56+
5. **Manage Frames (Bottom Panel)**
57+
- **Auto-save:** Your work is saved to the database automatically.
58+
- **Create:** Click the **+** button to add a new empty frame.
59+
- **Edit Details:** Assign a **Name** and **Duration** (in milliseconds) for each frame using the inputs above the frame list.
60+
- **Reorder:** Drag and drop frame thumbnails to change their sequence.
61+
- **Load/Delete:** Use the **Load** and **Del** buttons on each thumbnail to switch between frames or remove them.
9862

99-
- **`ui.expose_api('POST', '/export_frames', export_frames)`**: Generates C++ code arrays from selected frames for use in Arduino sketches.
63+
6. **Create Animations**
64+
- Switch the mode to **Animations** using the radio buttons in the bottom panel.
65+
- Select multiple frames by clicking on their thumbnails (they will highlight).
66+
- Click the **Play Animation** button below the grid to preview the sequence on the board.
10067

101-
- **`Bridge.call("draw", frame_bytes)`**: Sends frame data to the Arduino sketch to update the LED matrix display.
68+
7. **Export Code**
69+
- Toggle the **Code panel** switch in the top right header to view the C++ code for the current frame or animation in real-time.
70+
- Click the **Export .h** button to download a header file containing your selected designs, ready to be included in an Arduino sketch.
10271

103-
- **`AppFrame` class**: Custom extension of `arduino.app_utils.Frame` that adds metadata like frame name, position, duration, and database persistence methods.
104-
105-
### 💻 Frontend (`app.js` + `index.html`)
106-
107-
The web interface provides an interactive pixel grid editor and frame management tools.
108-
109-
- **Pixel Grid**: An 8×13 canvas that responds to mouse clicks and drag operations for painting pixels.
110-
111-
- **Brightness Control**: A slider (0-7) that controls the current brush brightness level.
112-
113-
- **Frame List**: Displays all saved frames in a sidebar with options to load, delete, or reorder them.
114-
115-
- **Animations Mode**: Allows creating and managing animation sequences by dragging frames into animation containers.
72+
## How it Works
11673

117-
- **Transform Tools**: Buttons for applying transformations (invert, rotate 180°, flip horizontal/vertical).
74+
The LED Matrix Painter relies on a synchronized data flow between the browser, the Python backend, and the hardware.
11875

119-
- **Export Modal**: Generates and displays C++ code for selected frames or animations.
76+
**High-level data flow:**
12077

121-
- **HTTP API calls**: Uses `fetch()` to communicate with the Python backend for all frame operations (save, load, delete, transform, export, play).
78+
```
79+
Web Browser ──► HTTP API ──► Python Backend ──► Router Bridge ──► Arduino Sketch
80+
│ │
81+
▼ ▼
82+
SQLite Database LED Matrix Display
83+
```
12284

123-
### 🔧 Hardware (`sketch.ino`)
85+
1. **Web Interface**: The `app.js` script captures clicks on the grid. It debounces these events and sends the pixel data to the backend via the `/persist_frame` endpoint.
86+
2. **Python Backend**:
87+
* **Data Model**: The `AppFrame` class normalizes the data, converting between frontend JSON, database records, and hardware byte arrays.
88+
* **Persistence**: The `store.py` module uses `SQLStore` to save the frame data to a `frames` table in a SQLite database.
89+
* **Bridge**: The `main.py` script sends the raw byte array to the board via `Bridge.call("draw", frame_bytes)`.
90+
3. **Arduino Sketch**: The sketch receives the raw byte data and uses the `Arduino_LED_Matrix` library to render the grayscale image.
12491

125-
The Arduino code handles LED matrix control and Router Bridge communication.
92+
## Understanding the Code
12693

127-
- **`matrix.begin()`**: Initializes the Arduino_LED_Matrix library for controlling the UNO Q LED matrix.
94+
### 🔧 Backend (`main.py`, `store.py` & `app_frame.py`)
95+
96+
The Python backend manages the application logic, database, and hardware communication.
97+
98+
- **Data Model (`app_frame.py`)**: The `AppFrame` class is the core data structure that acts as a bridge between the different components. It extends the base `Frame` class to add application-specific metadata like `id`, `name`, `position`, and `duration`. It handles three distinct data contracts:
99+
- **API Contract**: `to_json()` / `from_json()` formats data for the web frontend.
100+
- **Database Contract**: `to_record()` / `from_record()` formats data for `SQLStore` storage.
101+
- **Hardware Contract**: `to_board_bytes()` packs pixels into the specific byte format expected by the Arduino sketch.
102+
103+
```python
104+
class AppFrame(Frame):
105+
def to_record(self) -> dict:
106+
"""Convert to a database record dict for storage."""
107+
return {
108+
"id": self.id,
109+
"name": self.name,
110+
"rows": json.dumps(self.arr.tolist()), # Serialize pixels to JSON string
111+
"brightness_levels": int(self.brightness_levels),
112+
# ...
113+
}
114+
```
128115

129-
- **`matrix.setGrayscaleBits(8)`**: Configures the matrix to accept 8-bit brightness values (0-255) for each pixel.
116+
- **Initialization**:
117+
- `designer = FrameDesigner()`: Initializes the frame designer utility from `arduino.app_utils`, which provides the logic for transformation operations (invert, rotate, flip).
118+
- `store.init_db()`: Creates the SQLite database and tables for storing frames if they don't exist.
119+
120+
- **API Endpoints**: The backend exposes several HTTP endpoints using `ui.expose_api` to handle frontend requests:
121+
- `POST /persist_frame`: Saves or updates frames in the database and updates the board.
122+
- `POST /load_frame`: Loads a specific frame by ID or retrieves the last edited frame.
123+
- `GET /list_frames`: Returns all saved frames to populate the bottom panel.
124+
- `POST /play_animation`: Sends a sequence of frames to the Arduino to play as an animation.
125+
- `POST /transform_frame`: Applies geometric transformations to the pixel data.
126+
- `POST /export_frames`: Generates the C++ header file content.
127+
128+
- **Hardware Update**: The `apply_frame_to_board` function sends the visual data to the microcontroller via the Bridge.
129+
130+
```python
131+
# main.py
132+
def apply_frame_to_board(frame: AppFrame):
133+
"""Send frame bytes to the Arduino board."""
134+
frame_bytes = frame.to_board_bytes()
135+
Bridge.call("draw", frame_bytes)
136+
```
130137

131-
- **`Bridge.begin()`**: Initializes Router Bridge for receiving commands from the Python application.
138+
- **Code Generation**: The `AppFrame` class generates the C++ code displayed in the UI. It formats the internal array data into `uint32_t` hex values.
132139

133-
- **`Bridge.provide("draw", draw)`**: Registers the `draw` function to be callable from Python, which accepts frame data and renders it on the matrix.
140+
```python
141+
# app_frame.py
142+
def to_c_string(self) -> str:
143+
c_type = "uint32_t"
144+
parts = [f"const {c_type} {self.name}[] = {{"]
145+
# Converts pixel brightness data to uint32_t hex format
146+
parts.append("};")
147+
return "\n".join(parts)
148+
```
134149

135-
- **`Bridge.provide("play_animation", play_animation)`**: Registers the animation playback function that accepts multiple frames and plays them sequentially.
150+
### 🔧 Arduino Component (`sketch.ino`)
136151

137-
- **`matrix.draw(frame.data())`**: Renders a single frame on the LED matrix using raw byte data.
152+
The sketch is designed to be a passive renderer, accepting commands from the Python backend.
138153

139-
- **`matrix.loadWrapper()` + `matrix.playSequence()`**: Loads an animation sequence and plays it on the LED matrix.
154+
- **Grayscale Setup**: The matrix is initialized with 8-bit grayscale support to allow for varying brightness levels.
140155

141-
## Frame Data Format
156+
```cpp
157+
void setup() {
158+
matrix.begin();
159+
// configure grayscale bits to 8 so the display can accept 0..255 brightness
160+
matrix.setGrayscaleBits(8);
161+
Bridge.begin();
162+
// ...
163+
}
164+
```
142165

143-
Frames are stored as 8×13 arrays where each value represents LED brightness (0-255):
166+
- **Drawing**: The `draw` provider accepts a vector of bytes and renders it directly.
144167

145168
```cpp
146-
// Example frame in C++ format
147-
const uint8_t frame[][12] = {
148-
{255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255},
149-
{0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0},
150-
// ... 6 more rows
151-
};
169+
void draw(std::vector<uint8_t> frame) {
170+
matrix.draw(frame.data());
171+
}
152172
```
153173
154-
For animations, frames are stored as `uint32_t` arrays compatible with the Arduino_LED_Matrix library:
174+
### 🔧 Frontend (`app.js`)
155175
156-
```cpp
157-
const uint32_t animation[][5] = {
158-
{0x12345678, 0x9abcdef0, 0x12345678, 0x9abcdef0, 1000}, // Frame 1, 1000ms duration
159-
{0xfedcba98, 0x76543210, 0xfedcba98, 0x76543210, 1000}, // Frame 2, 1000ms duration
160-
};
176+
The JavaScript frontend handles the UI logic and data synchronization.
177+
178+
- **Auto-Persist**: To provide a responsive experience, changes are saved automatically after a short delay (debounce), updating both the database and the board simultaneously.
179+
180+
```javascript
181+
// Unified persist: save to DB and update board together
182+
function schedulePersist(){
183+
if (persistTimeout) clearTimeout(persistTimeout);
184+
persistTimeout = setTimeout(()=> {
185+
persistFrame();
186+
persistTimeout = null;
187+
}, AUTO_PERSIST_DELAY_MS);
188+
}
161189
```
198 KB
Loading

0 commit comments

Comments
 (0)