e553cf7835dd2a80311ca0b437f955dd6fa85c8f
ESP32-S3 TQ Printer Controller (Wi-Fi REST API)
ESP32-S3 works as a Wi-Fi REST controller and replaces Android App logic:
- REST client -> ESP32-S3 (
esp_http_server) - ESP32-S3 -> onboard direct thermal printer driver
Features
- Wi-Fi STA-only mode (configured SSID/password; no SoftAP fallback)
- Built-in minimal web UI at
/for voice status and MIC_KEY guidance - Direct thermal printer control with sensor precheck
- Async print queue with jobs (
queued/running/success/failed/canceled) - Printer precheck (paper / battery / temperature)
- Built-in UTF-8 text rendering with Chinese support (GB2312 character set, 16x16 bitmap)
- Push2talk multimodal voice interaction over DashScope WebSocket:
- ES8311 microphone/speaker via
esp_codec_dev - raw-opus uplink/downlink via
esp_audio_codec - WebSocket transport via
esp_websocket_client
- ES8311 microphone/speaker via
- REST APIs:
- Root:
GET /
- Health/connection:
GET /v1/healthPOST /v1/printer/connectPOST /v1/printer/disconnectGET /v1/printer/status
- Print:
POST /v1/print/rasterPOST /v1/print/image(JSON prompt -> generate image -> print, also compatible with direct PNG binary upload)POST /v1/print/text
- Jobs:
GET /v1/jobsGET /v1/jobs/{id}
- Voice:
GET /v1/voice/statusPOST /v1/voice/session/startPOST /v1/voice/session/stopPOST /v1/voice/tap/startPOST /v1/voice/tap/cancel
- Root:
Build
Prerequisites:
- ESP-IDF v5.x
- ESP32-S3 board
cd ai_printer
idf.py set-target esp32s3
idf.py menuconfig
idf.py build
Config
In menuconfig -> TQ Controller Config:
TQ_WIFI_PROV_SOFTAP_SSIDTQ_WIFI_PROV_SOFTAP_PASSWORDTQ_WIFI_PROV_SOFTAP_CHANNELTQ_WIFI_PROV_SOFTAP_MAX_CONNTQ_HTTP_PORTTQ_API_KEY(optional)- Z-Image HTTP auth and model fields:
TQ_Z_IMAGE_API_KEY(optional, empty means fallback toTQ_VOICE_API_KEY)TQ_Z_IMAGE_API_ENDPOINTTQ_Z_IMAGE_MODELTQ_Z_IMAGE_DEFAULT_SIZETQ_Z_IMAGE_TIMEOUT_MSTQ_Z_IMAGE_DOWNLOAD_TIMEOUT_MS
- Voice WebSocket auth and app fields:
TQ_VOICE_API_KEYTQ_VOICE_WORKSPACE_IDTQ_VOICE_APP_ID
- Voice audio and codec fields:
TQ_VOICE_SAMPLE_RATETQ_VOICE_OPUS_FRAME_MSTQ_VOICE_OPUS_BITRATE_KBPSTQ_VOICE_I2C_*/TQ_VOICE_I2S_*TQ_VOICE_CODEC_*
- Board GPIO map fields:
TQ_POWER_*,TQ_LED_*,TQ_SCREEN_*,TQ_PRINT_*,TQ_SPI_*TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT/TQ_KEY_PRINT_BOOST_ACTIVE_HIGH
- ST7789 screen fields:
TQ_SCREEN_ENABLETQ_SCREEN_TEST_PATTERN_ON_BOOTTQ_SCREEN_PIXEL_CLOCK_HZTQ_SCREEN_SPI_MODETQ_SCREEN_H_RES/TQ_SCREEN_V_RESTQ_SCREEN_DRAW_LINESTQ_SCREEN_COLOR_ORDER_BGRTQ_SCREEN_BACKLIGHT_ACTIVE_HIGHTQ_SCREEN_MIRROR_*/TQ_SCREEN_SWAP_XYTQ_SCREEN_X_GAP/TQ_SCREEN_Y_GAPTQ_SCREEN_RESET_PIN
Board pin assignment reference:
When TQ_API_KEY is not empty, each request must include:
X-API-Key: <your-key>
Wi-Fi provisioning behavior:
- Device first tries saved STA credentials from NVS (
wifi_cfgnamespace). - If no valid credentials exist or STA connect fails, device starts a SoftAP portal.
- Connect to the SoftAP and open
http://192.168.4.1; the static page auto-scans nearby SSIDs for selection. - Provisioning page supports Chinese/English switching and remembers the selected language in browser local storage.
- Credentials are written to NVS only after STA connect succeeds.
Flash
cd ai_printer
idf.py -p /dev/tty.usbmodemXXXX flash monitor
Web UI
Open the controller IP in a browser:
http://<esp-ip>/
The page provides a minimal voice console:
- MIC_KEY(IO14) trigger guide (press to talk, release to process)
- voice status display
REST Examples
Health
curl http://<esp-ip>/v1/health
Connect printer
curl -X POST http://<esp-ip>/v1/printer/connect \
-H 'Content-Type: application/json' \
-d '{"timeout_ms":15000}'
# Optional explicit backend field:
# -d '{"backend":"direct","timeout_ms":15000}'
Raster print
curl -X POST http://<esp-ip>/v1/print/raster \
-H 'Content-Type: application/json' \
-d '{
"width":384,
"height":200,
"density":"中等",
"encoding":"base64_msb_1bpp",
"data":"<BASE64_BYTES>"
}'
Image print (generate with prompt, then print)
curl -X POST http://<esp-ip>/v1/print/image \
-H 'Content-Type: application/json' \
-d '{
"prompt":"一只坐在窗边的橘猫,午后阳光,胶片质感,写实风格。",
"size":"1120*1440",
"prompt_extend":false,
"threshold":160,
"max_height":2200,
"scale_to_width":true,
"invert":false,
"density":"中等",
"timeout_ms":45000,
"fetch_timeout_ms":15000
}'
Image print (direct PNG upload, backward-compatible)
curl -X POST 'http://<esp-ip>/v1/print/image?scale_to_width=1&threshold=160&invert=0&max_height=2200&density=medium' \
-H 'Content-Type: image/png' \
--data-binary @./sample.png
Request constraints:
- Request body limit: 4 MB
- Suggested image size: <= 3 MB when using direct PNG upload compatibility mode
Text print
curl -X POST http://<esp-ip>/v1/print/text \
-H 'Content-Type: application/json' \
-d '{
"text":"欢迎使用TQ打印机\\n订单号: A1024\\n谢谢惠顾",
"density":"中等",
"scale":2,
"line_spacing":2,
"max_height":1800
}'
Jobs
curl http://<esp-ip>/v1/jobs
curl http://<esp-ip>/v1/jobs/1
Voice push2talk
Start session:
curl -X POST http://<esp-ip>/v1/voice/session/start
Press-to-talk start (sends SendSpeech when entering Listening):
curl -X POST http://<esp-ip>/v1/voice/tap/start
Release-to-stop (sends StopSpeech):
curl -X POST http://<esp-ip>/v1/voice/tap/cancel
Get voice status:
curl http://<esp-ip>/v1/voice/status
Stop session:
curl -X POST http://<esp-ip>/v1/voice/session/stop
Notes
base64_msb_1bppuses Android-compatible bit order (MSB first)./v1/print/imagesupports two modes: JSON prompt generation (DashScope Z-Image) and rawimage/pngupload (no base64 wrapper)./v1/print/textsupports UTF-8 Chinese via embedded 16x16 GB2312 glyphs.- Characters outside embedded glyph set are rendered as square fallback boxes.
- Large buffers for image upload/decode prefer PSRAM first, then fallback to internal RAM.
- Partition table uses
partitions.csvwith a 4MBfactoryapp partition on 16MB flash modules. - Voice flow uses
push2talk: client sendsSendSpeechto start uplink andStopSpeechon release to end input. - Voice flow is now single-round: after recognition/response completes,
LocalRespondingEndedandStopare sent after local playback drain. - Voice flow blocks starting the next round while voice is Thinking/Responding, marker image generation is running, or printer queue is still busy.
Font Assets
Embedded files:
components/domain/assets/fonts/cn16_index.bincomponents/domain/assets/fonts/cn16_glyphs.bin
Regenerate from your own CJK font:
cd ai_printer
python3 -m pip install --user pillow
python3 tools/gen_cn16_font.py \
--font app/src/main/assets/fonts/msyh.ttc \
--font-index 0
Description
Languages
C
67.4%
Python
29.2%
HTML
2.9%
CMake
0.5%