fix bugs, harden server, strip-based dithering, update docs

Firmware:
- fix 90° rotation (was transpose)
- fix Adafruit_Thermal constructor (spurious DTR pin arg)
- wire up uploadImage; heatInterval now applied
- PSRAM-free strip dithering: 2-row buffers (~1KB) replace 153KB PSRAM alloc
- consolidate all pins into config.h; BUTTON_PIN → Config::PIN_BUTTON
- constrain contrast/brightness/heat in settings save handler
- uploadImage size param int → size_t

Server:
- canonicalize upload_dir at startup (fixes path traversal guard)
- path traversal guard in serve_image
- replace unwrap in gallery_data with error handling
- IMAGE_WIDTH/IMAGE_HEIGHT named constants

Gallery:
- innerHTML → createElement (XSS-safe)
- encodeURIComponent on image URLs
- replace("_"," ") → regex /_/g

Docs: rewrite README, clarify GUEST_MANUAL placeholders
This commit is contained in:
2026-06-18 11:23:05 +02:00
parent 2f827c168d
commit 4ec723ef40
12 changed files with 263 additions and 219 deletions

169
README.md
View File

@@ -1,82 +1,131 @@
# Party Cam: Instant Thermal Camera & Digital Gallery
A DIY point-and-shoot camera that prints dithered lo-fi photos on thermal receipts and instantly uploads high-res copies to a local Rust server.
A DIY point-and-shoot that prints dithered lo-fi photos on thermal receipt paper and simultaneously uploads captures to a local Rust web server.
---
## Hardware BOM
- **ESP32-CAM**
- **Thermal Printer** (TTL Serial, 5V-9V)
- **Batteries** (Recommended: 2x 18650 Li-Ion in series = 7.4V, powering printer directly + buck converter to 5V for ESP32)
- **Button** (Tactile switch)
- **Programmer**: FTDI Adapter or Arduino Uno (for flashing)
| Part | Notes |
| :--- | :--- |
| ESP32-CAM (AI-Thinker) | Camera + WiFi module |
| Thermal Printer | TTL serial, 5V9V |
| 2× 18650 Li-Ion | In series = 7.4V; powers printer directly + 5V buck converter for ESP32 |
| Tactile button | Shutter trigger |
| FTDI adapter or Arduino Uno | For flashing only |
---
## Wiring
### 1. Camera & Printer
| ESP32-CAM Pin | Component | Function |
| :--- | :--- | :--- |
| **5V** | PSU 5V | Power in |
| **GND** | PSU GND | Common ground |
| **GPIO 12** | Button | Shutter (other leg → GND) |
| **GPIO 14** (U1T) | Printer RX | Data to printer |
| **GPIO 15** (U1R) | Printer TX | Data from printer |
| **GPIO 4** | Flash LED | Optional built-in flash |
| ESP32-CAM Pin | Component | Function |
| :---------------- | :--------- | :--------------------------------- |
| **5V** | PSU 5V | Power In |
| **GND** | PSU GND | Common Ground |
| **GPIO 12** | Button | Trigger (Connect other leg to GND) |
| **GPIO 14** (U1T) | Printer RX | Data TO Printer |
| **GPIO 15** (U1R) | Printer TX | Data FROM Printer |
| **GPIO 4** | Flash LED | (Optional) Built-in Flash |
### 2. Flashing (Using Arduino Uno as Bridge)
_See "How to Flash" section below._
All pin assignments are in `include/config.h`. Change them there if your wiring differs.
---
## Setup Guide
## Server Setup
### A. The Server (Rust)
Run the server on any machine on the same local network as the camera. It receives uploads and hosts the gallery.
The server receives images and hosts the gallery for guests.
### Option A: Docker Compose (recommended)
1. **Install Rust:** `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`
2. **Configure:**
Create a `.env` file in the `server` folder:
```ini
SERVER_HOST=0.0.0.0
SERVER_PORT=3000
GALLERY_PASSWORD=partytime
UPLOAD_DIR=./uploads
```
3. **Run:**
```bash
cd server
cargo run --release
```
_Server will listen on port 3000._
```bash
cd server
docker compose up -d
```
### B. The Camera (Firmware)
Images are saved to `server/party_images/` on the host. To change the gallery password, edit `GALLERY_PASSWORD` in `compose.yml`.
1. **Open Project:** Open this folder in VS Code with the PlatformIO extension.
2. **Flash Mode Wiring (Arduino Uno Method):**
- **Arduino RESET** -> **Arduino GND** (Bypasses Arduino chip).
- **Arduino Pin 0 (RX)** -> **ESP32 U0R** (Direct passthrough).
- **Arduino Pin 1 (TX)** -> **ESP32 U0T** (Direct passthrough).
- **ESP32 IO0** -> **ESP32 GND** (Enables Download Mode).
3. **Upload:**
- Connect via USB.
- Click "Upload".
- Press the **RST** button on ESP32 when "Connecting..." appears.
4. **Run Mode:**
- **Disconnect IO0 from GND.**
- Press **RST** to boot.
### Option B: Cargo (dev)
```bash
cd server
cargo run --release
```
Requires a `.env` file in the `server/` directory:
```ini
SERVER_HOST=0.0.0.0
SERVER_PORT=3000
GALLERY_PASSWORD=partytime
UPLOAD_DIR=./uploads
```
`GALLERY_PASSWORD` is required — the server panics at startup without it.
---
## Configuration (First Boot)
## Firmware Setup
1. **Power on** the camera.
2. Connect your phone to the WiFi network: `PartyCam-Setup`.
3. A captive portal should open (or go to `192.168.4.1`).
4. **WiFi:** Select the venue's WiFi (or your phone hotspot).
5. **Server URL:** Enter your laptop's IP (e.g., `http://192.168.1.15:3000/upload`).
6. **Settings:** Adjust Contrast/Heat if the print is too faint or dark.
7. **Save & Reboot.**
### 1. Configure
The camera is now live!
Check `platformio.ini` and make sure `board` matches your actual hardware. The default is `4d_systems_esp32s3_gen4_r8n16` — change it to `ai_thinker_esp32-cam` if using a standard ESP32-CAM. Camera pin definitions in `include/config.h` are pre-set for AI-Thinker.
### 2. Flash (Arduino Uno as bridge)
Wire the Uno as a passthrough:
- Arduino RESET → Arduino GND (bypasses the Uno chip)
- Arduino Pin 0 (RX) → ESP32 U0R
- Arduino Pin 1 (TX) → ESP32 U0T
- **ESP32 IO0 → ESP32 GND** (enables download mode)
Click **Upload** in PlatformIO, then press RST on the ESP32 when "Connecting…" appears.
### 3. Boot
Disconnect IO0 from GND, press RST. The camera boots.
---
## First Boot Configuration
1. Power on. Camera broadcasts WiFi AP: **`PartyCam-Setup`**.
2. Connect your phone — a captive portal opens automatically (or go to `192.168.4.1`).
3. Select the venue WiFi and enter the password.
4. Set the **Upload URL** to `http://<server-machine-ip>:3000/upload`.
5. Adjust contrast/heat to taste. **Save & Reboot.**
The portal times out after 3 minutes. If it closes before you save, the camera boots in offline mode (printing still works, upload disabled).
---
## Settings Page
After first boot the settings page is always reachable at:
```
http://partycam.local
```
(or the camera's IP address on port 80). Use it to adjust contrast, brightness, flip/mirror, heat density, and the upload URL.
---
## Gallery
```
http://<server-ip>:3000/gallery
```
Open from any device on the same network. The browser will prompt for the gallery password. New photos appear automatically — the page polls every 5 seconds.
---
## Customisation
| What | Where |
| :--- | :--- |
| Pin assignments | `include/config.h` |
| Printer baud rate | `include/config.h``BAUD_RATE` |
| Default camera settings | `include/settings_service.h``AppSettings` struct defaults |
| Gallery password | `compose.yml` or `.env` |
| Capture resolution | `src/camera_service.cpp``config.frame_size` (update `IMAGE_WIDTH`/`IMAGE_HEIGHT` in `server/src/main.rs` to match) |