Control a QR204 ESC/POS thermal printer via a SEEED XIAO ESP32-C3 over MQTT, fully integrated with Home Assistant.
Home Assistant / CLI
│ MQTT
▼
SEEED XIAO ESP32-C3 ──UART──▶ QR204 Thermal Printer
| XIAO ESP32-C3 | QR204 Printer | Notes |
|---|---|---|
| 5V | VCC (red) | Printer needs 5–9V |
| GND | GND (black) | Common ground |
| D6 / GPIO21 | RX (white) | ESP32 TX → Printer RX |
| D7 / GPIO20 | TX (green) | ESP32 RX ← Printer TX |
Pin labels: On the XIAO ESP32-C3 silkscreen, D6 is the TX pin (GPIO21) and D7 is the RX pin (GPIO20). Wire D6 to the printer's RX line and D7 to the printer's TX line.
Baud rate: The QR204 typically ships at 9600 baud. Check your printer's DIP switches or config sheet. Update
PRINTER_BAUDinmain.cppif yours is different (some units ship at 115200).
- PlatformIO (VS Code extension or CLI)
- SEEED XIAO ESP32-C3 board
- Clone / copy the
printer_esp32/folder. - Edit
src/secrets.hwith your WiFi and MQTT credentials. - Build & upload:
cd printer_esp32 pio run --target upload pio device monitor # watch serial output
- On first boot you should see
MQTT connectedin the serial monitor and the printer status sensor in HA will showonline.
Ensure the Mosquitto broker add-on (or any MQTT broker) is running and configured in HA's MQTT integration.
Copy the contents of printer_integration.yaml into your configuration.yaml, or use the packages feature:
# configuration.yaml
homeassistant:
packages:
printer: !include packages/printer.yamlThen place printer_integration.yaml as config/packages/printer.yaml.
- Replace
YOUR_MEALIE_HOST,YOUR_MEALIE_API_TOKEN - Replace
YOUR_HA_LONG_LIVED_TOKEN(Settings → Profile → Long-Lived Access Tokens) - Set your WiFi SSID/password in the
printer_wifi_qrscript
Developer Tools → YAML → Check Configuration → Restart
| Topic | Payload | Description |
|---|---|---|
homeassistant/printer/cmd/text |
{"text":"...","bold":false,"size":1,"align":"left","cut":true} |
Print plain text |
homeassistant/printer/cmd/shopping |
{"title":"Shopping List","items":["Milk","Eggs"],"cut":true} |
Print shopping list |
homeassistant/printer/cmd/recipe |
{"name":"...","servings":"4","time":"30m","ingredients":[...],"steps":[...],"cut":true} |
Print recipe |
homeassistant/printer/cmd/qrcode |
{"data":"https://...","label":"Scan me","size":6,"cut":true} |
Print QR code |
homeassistant/printer/cmd/image |
{"bitmap":[...],"width":384,"height":200,"cut":true} |
Print bitmap image |
homeassistant/printer/cmd/raw |
base64-encoded ESC/POS bytes | Raw printer commands |
| Topic | Values | Description |
|---|---|---|
homeassistant/printer/status |
online / offline / printing / error |
Printer state |
homeassistant/printer/paper |
ok / low / out |
Paper status (future) |
For image printing and Mealie integration, use the printer_helper.py script.
pip install paho-mqtt Pillow requestsexport MQTT_HOST=192.168.1.x
export MQTT_USER=mqtt_user
export MQTT_PASS=mqtt_password
export MEALIE_HOST=http://192.168.1.x:9000
export MEALIE_TOKEN=your_mealie_api_token
export HA_HOST=http://192.168.1.x:8123
export HA_TOKEN=your_ha_long_lived_token# Print an image
python printer_helper.py image photo.jpg
# Print a Mealie recipe by slug
python printer_helper.py recipe chocolate-chip-cookies
# Print the HA shopping list
python printer_helper.py shopping
# Print a WiFi QR code
python printer_helper.py wifi "MyHomeWiFi" "password123"
# Print text
python printer_helper.py text "Hello from the terminal!"Add to configuration.yaml:
shell_command:
print_image: "python3 /config/printer_helper.py image {{ path }}"
print_recipe: "python3 /config/printer_helper.py recipe {{ slug }}"Two methods are available so you never need to physically touch the device again after the first USB flash.
PlatformIO pushes the compiled binary directly to the ESP32 over WiFi using the espota protocol.
# Build and upload wirelessly — use the _ota environment defined in platformio.ini
pio run -e seeed_xiao_esp32c3_ota -t upload- Set
upload_portinplatformio.inito your device's IP (printed to serial on boot). - Set
--authinupload_flagsto matchOTA_PASSWORDinsecrets.h. - The device appears as
esp32-printerin Arduino IDE's network ports too.
Host the compiled .bin anywhere on your local network (a simple Python HTTP server works fine), then trigger the update via MQTT. The ESP fetches and flashes itself, then reboots.
Step 1 — Serve the binary:
# In the project folder after building
cd .pio/build/seeed_xiao_esp32c3
python3 -m http.server 8080Step 2 — Publish the OTA command:
# Via mosquitto_pub
mosquitto_pub -h 192.168.1.x -u mqtt_user -P mqtt_pass \
-t homeassistant/printer/cmd/ota \
-m '{"url":"http://192.168.1.YOUR_PC:8080/firmware.bin"}'
# Or via the HA Developer Tools → Services → mqtt.publishStep 3 — Watch progress:
homeassistant/printer/status→"ota"while flashinghomeassistant/printer/ota/progress→0…100homeassistant/printer/status→"online"after reboothomeassistant/printer/version→ updated version string
If you ever need a clean reboot without a firmware change:
mosquitto_pub -h 192.168.1.x -u mqtt_user -P mqtt_pass \
-t homeassistant/printer/cmd/restart -m "reboot"# Lovelace card
type: entities
entities:
- entity: sensor.printer_status
name: Printer Status
- entity: sensor.printer_paper
name: Paper Level
type: button
tap_action:
action: call-service
service: script.printer_shopping_list
name: "🛒 Print Shopping List"
icon: mdi:printerAdding a new print type is straightforward:
-
ESP32 (main.cpp): Add a new
#define MQTT_CMD_MYTYPEtopic, subscribe to it inmqttReconnect(), add a handler inmqttCallback(), write aprintMyType(JsonDocument&)function. -
Home Assistant: Add a new
script:entry inprinter_integration.yamlthat publishes to the new topic. -
Python helper: Add a
cmd_mytype()function and a new subparser.
- 📅 Print today's calendar events (HA calendar integration)
- 🌤️ Print weather forecast
- 📦 Print a delivery tracking summary
- 🎫 Print event tickets / reminders
- 🏷️ Print labels with barcodes (EAN-13 via ESC/POS
GS k) - 🤖 Print AI-generated jokes or quotes via Claude API
| Symptom | Likely cause | Fix |
|---|---|---|
| No output, printer LED solid | Wrong baud rate | Check DIP switches, update PRINTER_BAUD |
| Garbled output | TX/RX crossed | Swap D6/D7 wiring |
| MQTT won't connect | Broker IP/credentials | Check secrets.h, test with MQTT Explorer |
| Image looks terrible | Contrast too low | Pass through Pillow's autocontrast in helper |
| QR code won't scan | Data too long or size too small | Reduce data length or increase size (max 8) |
status stays offline |
ESP32 not booting | Check USB CDC boot flag in platformio.ini |