- Python 47%
- CSS 28.4%
- HTML 24.6%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| static | ||
| templates | ||
| .gitignore | ||
| app.py | ||
| printer.py | ||
| README.md | ||
| requirements.txt | ||
Thermal Printer Web App
A small Flask app for printing grocery lists and short notes to a USB ESC/POS thermal printer (built for an FEC-80T on a Raspberry Pi, but works with any 80mm ESC/POS printer).
Features
- Grocery list — type items, print with checkboxes
- Short messages — leave notes for roommates with optional "To / From"
- Custom logo — upload an image, auto-resized and dithered for clean thermal output
- Test page — verify formatting and line width
- Mobile-friendly UI
Setup
# From your existing venv (~/escpos-env)
source ~/escpos-env/bin/activate
# Copy this folder somewhere, then:
cd thermal_printer_app
pip install -r requirements.txt
Run (development)
The Flask dev server is fine for tinkering:
python app.py
Run (production / Gunicorn)
gunicorn -w 1 --threads 4 -b 0.0.0.0:5000 app:app
Important: always use -w 1 (one worker process). The USB printer is a serialized
hardware resource — multiple worker processes would race for it.
--threads 4 lets the single worker handle multiple HTTP requests concurrently
(HTML + CSS + favicon load in parallel). Without it, page loads feel sluggish.
Concurrent print jobs from different threads are serialized by an internal lock in
printer.py.
Then open http://<your-pi-ip>:PORT from your phone or laptop.
Configuration
Edit the top of printer.py:
PRINTER_CONFIG = {
"idVendor": 0x0525,
"idProduct": 0xA700,
"in_ep": 0x81,
"out_ep": 0x02,
"profile": "default",
}
LINE_WIDTH = 48 # chars per line at Font A — try 42 if 48 wraps
MAX_LOGO_WIDTH = 400 # logo pixel width before printing
If your printer's separators wrap to a second line, drop LINE_WIDTH to 42. Crosscheck with printer manual.
Running on boot (optional)
Create /etc/systemd/system/thermalprint.service:
[Unit]
Description=Thermo Printer Web App
After=network.target
[Service]
Type=simple
User=$USER
WorkingDirectory=/home/$USER/thermal_printer_app
ExecStart=/home/$USER/escpos-env/bin/gunicorn -w 1 --threads 4 -b 0.0.0.0:5000 --access-logfile - app:app
Restart=on-failure
[Install]
WantedBy=multi-user.target
Then:
sudo systemctl daemon-reload
sudo systemctl enable --now thermalprint
# Watch logs
journalctl -u thermalprint -f
File layout
thermal_printer_app/
├── app.py # Flask routes
├── printer.py # ESC/POS print functions
├── requirements.txt
├── config.json # Created at runtime (logo settings)
├── static/
│ ├── style.css
│ └── logos/ # Uploaded logos go here
└── templates/
├── base.html
├── index.html
├── grocery.html
├── message.html
└── settings.html
Notes
- The app binds to
0.0.0.0:PORTso anything on your LAN can reach it. Don't expose it directly to the public internet — there is no authentication. - A
.flask_secretfile is auto-generated on first run (used to sign session cookies for flash messages). Keep it private; don't commit it. - For remote access, use Tailscale, WireGuard, or an SSH tunnel rather than port-forwarding.