A modern, feature-rich firmware for the Waveshare ESP32-S3-PhotoPainter that replaces the stock firmware with a powerful RESTful API, web interface, and significantly better image quality. This firmware provides superior image management, automatic rotation handling, drag-and-drop uploads, and intelligent power management.
Product Page: Waveshare ESP32-S3-PhotoPainter
Our firmware uses a measured color palette for superior image rendering compared to the stock firmware. The images below show simulated results of what you'll see on the actual e-paper display:
⚠️ Note: The stock algorithm may look acceptable on computer displays, but when flashed onto the actual e-paper device, the colors appear washed out and the image quality is significantly degraded. This is because the stock algorithm uses theoretical RGB values that don't match the physical characteristics of the e-paper display. Our enhanced algorithm with measured palette produces images that look much better on the actual device.
🎨 Try the Interactive Demo - Drag the slider to compare algorithms in real-time with your own images!
Why Our Algorithm is Better:
- ✅ Accurate Color Matching: Uses actual measured e-paper colors
- ✅ Better Dithering: Floyd-Steinberg algorithm with measured palette produces more natural color transitions
- ✅ No Over-Saturation: Avoids the washed-out appearance of theoretical palette matching
The measured palette accounts for the fact that e-paper displays show darker, more muted colors than pure RGB values. By dithering with these actual colors, the firmware makes better decisions about which palette color to use for each pixel, resulting in images that look significantly better on the physical display.
📖 Read the technical deep-dive on measured color palettes →
The firmware implements intelligent power management with configurable deep sleep behavior:
The device supports two power modes, configurable via the web interface:
Best for: Battery-powered operation, portable use
- LED Indicator: RED LED on when awake
- Auto-Sleep: Device enters deep sleep after 2 minutes of inactivity (on battery only)
- Auto-Rotate: Uses timer-based wake-up from deep sleep
- Web Interface: Accessible only when awake
- Wake-Up Methods:
- Boot button (GPIO 0) - wakes device and starts HTTP server
- Key button (GPIO 4) - wakes device and triggers image rotation
- Auto-rotate timer - wakes device at configured interval
- Power Consumption: Minimal (~10μA in deep sleep)
- Battery Life: Maximum (weeks to months depending on rotation interval)
Best for: Home Assistant integration
- LED Indicator: RED LED off (to save power)
- Auto-Sleep: Disabled when on battery (device stays awake with auto light sleep)
- Auto-Rotate: Uses active countdown timer
- Web Interface: Always accessible via HTTP
- Power Consumption: Higher (~40-80mA with auto light sleep)
- Battery Life: Significantly reduced (hours instead of weeks)
When USB Connected (regardless of deep sleep setting):
- Auto-sleep is disabled (device stays awake)
- Auto-rotate uses active countdown timer
- Web interface always accessible
When Running on Battery:
- Behavior depends on deep sleep setting (see modes above)
- Sleep timer reset by any HTTP interaction or button press
Access the web interface and navigate to Power & Auto-Rotate Settings:
- Check/uncheck "Enable Deep Sleep"
- Click "Save Settings"
- Setting is saved to non-volatile storage and persists across reboots
Power Consumption Warning: When deep sleep is disabled, a warning appears explaining the increased power consumption.
This firmware is designed for the Waveshare ESP32-S3-PhotoPainter.
Requirements:
- MicroSD card (formatted as FAT32)
- WiFi network (2.4GHz)
- USB-C cable for programming
Flash directly from your browser - no software installation required!
Requirements:
- Chrome, Edge, or Opera browser (Web Serial API support)
- USB-C cable to connect your ESP32-S3-PhotoPainter
- Just click "Connect & Flash" and follow the prompts!
Download the latest prebuilt firmware from the GitHub Releases page:
-
Quick Flash (Single File):
esptool.py --chip esp32s3 --port /dev/ttyUSB0 --baud 921600 write_flash 0x0 photoframe-firmware-merged.bin
-
Individual Files:
esptool.py --chip esp32s3 --port /dev/ttyUSB0 --baud 921600 \ write_flash 0x0 bootloader.bin \ 0x8000 partition-table.bin \ 0x10000 photoframe-api.bin
Replace /dev/ttyUSB0 with your serial port (e.g., /dev/cu.usbserial-* on macOS, COM3 on Windows).
This firmware has automatic light sleep enabled. If the device is not detected for flashing:
- Press and hold the PWR button for 5 seconds (until device powers off)
- Hold down the BOOT button and click the PWR button to enter download mode
- Run the flash command while in download mode
The device will remain in download mode and be detectable for flashing.
For developers: See DEV.md for build-from-source instructions and configuration options.
Important
Insert the MicroSD card before installing the firmware. If the card is not detected, the device will halt.
On first boot, the device automatically starts in WiFi provisioning mode if no credentials are saved:
- Connect to AP: The device creates a WiFi access point named
PhotoFrame-Setup - Open Browser: Connect to the AP and navigate to
http://192.168.4.1- Most devices will automatically open a captive portal
- Enter Credentials: Submit your WiFi SSID and password
- Live Connection Test: The device tests the connection in real-time
- Uses APSTA mode - you stay connected to the setup page during the test
- ✅ Success: Credentials are saved and device restarts
- ❌ Failure: Error message appears immediately - retry with correct credentials
- Auto-Connect: On subsequent boots, device connects automatically
Important Notes:
- Only 2.4GHz WiFi networks are supported (ESP32 limitation)
- WPA3-SAE security is supported
- Connection is tested before saving - invalid credentials are never saved
- You remain connected to the setup page during testing (no need to reconnect)
- To re-provision, erase flash with:
idf.py erase-flash
- Format SD card as FAT32
- Insert into device before powering on
- The device will automatically create
/sdcard/images/directory on first boot - You can pre-load BMP images (800x480, 7-color palette) into this directory
- After connecting to WiFi, access the device at:
- mDNS:
http://photoframe.local(recommended - works on most devices) - IP Address: Check serial monitor for the device's IP address
- mDNS:
- The web interface provides:
- Image gallery with thumbnails
- Drag-and-drop upload for JPG images
- Display control and image management
- Configuration for auto-rotate, brightness, and contrast
- Real-time battery status
Note: If photoframe.local doesn't work, use the IP address shown in the serial monitor.
Complete API documentation is available in API.md.
- AP not visible: Check serial monitor for errors, ensure device is powered on
- Captive portal doesn't open: Manually navigate to
http://192.168.4.1 - Connection test fails: Verify SSID and password are correct, then retry
- You stay connected to the setup page - no need to reconnect
- Error message appears within 15 seconds if credentials are wrong
- Wrong network saved: Erase flash with
idf.py erase-flashand re-provision - 2.4GHz only: ESP32 doesn't support 5GHz WiFi networks
- Ensure 2.4GHz WiFi network (ESP32 doesn't support 5GHz)
- Check serial monitor for connection status and IP address
- Try accessing via
http://photoframe.localor the IP address shown in logs
- Ensure SD card is formatted as FAT32
- Check SD card is properly inserted
- Try a different SD card (some cards are incompatible)
- Ensure the uploaded file is a valid JPG/JPEG image
- Check available SPIRAM (large images require significant memory)
- Monitor serial output for specific error messages
- If upload succeeds but conversion fails, check SD card space
Use the Node.js CLI tool to process images offline with the same pipeline as the webapp:
cd process-cli
npm install
# Process single image with device settings (recommended)
node cli.js input.jpg --device-parameters -o /path/to/sdcard/images/
# Process entire album directory structure
node cli.js ~/Photos/Albums --device-parameters -o /path/to/sdcard/images/
# Custom S-curve and saturation
node cli.js input.jpg --scurve-strength 0.8 --saturation 1.5 -o output/
# Preview mode (render with measured palette)
node cli.js input.jpg --render-measured -o preview/Features:
- Automatic detection: Processes single files or entire folder structures
- Device parameters: Fetch settings and calibrated palette from your device
- Batch processing: Automatically processes album subdirectories
- Dual output: BMP for display + JPEG thumbnail for web interface
Output:
photo.bmp- Processed image for e-paper display (theoretical palette)photo.jpg- Thumbnail for web interface
See process-cli/README.md for detailed usage.
This project is based on the ESP32-S3-PhotoPainter sample code. Please refer to the original project for licensing information.
- Original PhotoPainter sample: Waveshare ESP32-S3-PhotoPainter
- E-paper drivers: Waveshare
- ESP-IDF: Espressif Systems




