# 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 -> BLE printer (`TQPrinter` / `lyfPrinter`, `FFF2` write / `FFF1` notify) - Command compatibility: - Print path: `0x00~0x07` - OTA path: `0xA0~0xA4` ## Features - Wi-Fi STA-only mode (configured SSID/password; no SoftAP fallback) - Built-in minimal web UI at `/` for voice tap2talk - BLE central client auto-scan/connect to printer name - 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) - Tap2talk 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` - Android-compatible print flow: - `0x00` power on - `0x03` set print param - `0x04` chunked raster send + ACK - `0x00` power off - `0x02` feed paper - REST APIs: - Root: - `GET /` - Health/connection: - `GET /v1/health` - `POST /v1/printer/connect` - `POST /v1/printer/disconnect` - `GET /v1/printer/status` - Print: - `POST /v1/print/raster` - `POST /v1/print/image` (JSON prompt -> generate image -> print, also compatible with direct PNG binary upload) - `POST /v1/print/text` - Jobs: - `GET /v1/jobs` - `GET /v1/jobs/{id}` - Voice: - `GET /v1/voice/status` - `POST /v1/voice/session/start` - `POST /v1/voice/session/stop` - `POST /v1/voice/tap/start` - `POST /v1/voice/tap/cancel` ## Build Prerequisites: - ESP-IDF v5.x - ESP32-S3 board ```bash cd ai_printer idf.py set-target esp32s3 idf.py menuconfig idf.py build ``` ## Config In `menuconfig -> TQ Controller Config`: - `TQ_WIFI_PROV_SOFTAP_SSID` - `TQ_WIFI_PROV_SOFTAP_PASSWORD` - `TQ_WIFI_PROV_SOFTAP_CHANNEL` - `TQ_WIFI_PROV_SOFTAP_MAX_CONN` - `TQ_HTTP_PORT` - `TQ_API_KEY` (optional) - Z-Image HTTP auth and model fields: - `TQ_Z_IMAGE_API_KEY` (optional, empty means fallback to `TQ_VOICE_API_KEY`) - `TQ_Z_IMAGE_API_ENDPOINT` - `TQ_Z_IMAGE_MODEL` - `TQ_Z_IMAGE_DEFAULT_SIZE` - `TQ_Z_IMAGE_TIMEOUT_MS` - `TQ_Z_IMAGE_DOWNLOAD_TIMEOUT_MS` - Voice WebSocket auth and app fields: - `TQ_VOICE_API_KEY` - `TQ_VOICE_WORKSPACE_ID` - `TQ_VOICE_APP_ID` - Voice audio and codec fields: - `TQ_VOICE_SAMPLE_RATE` - `TQ_VOICE_OPUS_FRAME_MS` - `TQ_VOICE_OPUS_BITRATE_KBPS` - `TQ_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_ENABLE` - `TQ_SCREEN_TEST_PATTERN_ON_BOOT` - `TQ_SCREEN_PIXEL_CLOCK_HZ` - `TQ_SCREEN_SPI_MODE` - `TQ_SCREEN_H_RES` / `TQ_SCREEN_V_RES` - `TQ_SCREEN_DRAW_LINES` - `TQ_SCREEN_COLOR_ORDER_BGR` - `TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH` - `TQ_SCREEN_MIRROR_*` / `TQ_SCREEN_SWAP_XY` - `TQ_SCREEN_X_GAP` / `TQ_SCREEN_Y_GAP` - `TQ_SCREEN_RESET_PIN` Board pin assignment reference: - [`docs/gpio-map.md`](docs/gpio-map.md) When `TQ_API_KEY` is not empty, each request must include: ```text X-API-Key: ``` Wi-Fi provisioning behavior: - Device first tries saved STA credentials from NVS (`wifi_cfg` namespace). - 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 ```bash cd ai_printer idf.py -p /dev/tty.usbmodemXXXX flash monitor ``` ## Web UI Open the controller IP in a browser: ```text http:/// ``` The page provides a minimal voice console: - single-round tap2talk button - voice status display ## REST Examples ### Health ```bash curl http:///v1/health ``` ### Connect printer ```bash curl -X POST http:///v1/printer/connect \ -H 'Content-Type: application/json' \ -d '{"name":"TQPrinter","timeout_ms":15000}' # Legacy device name is also supported: # -d '{"name":"lyfPrinter","timeout_ms":15000}' ``` Auto-match a compatible printer (by BLE service `0xFFF0`): ```bash curl -X POST http:///v1/printer/connect \ -H 'Content-Type: application/json' \ -d '{"name":"*","timeout_ms":20000}' ``` ### Raster print ```bash curl -X POST http:///v1/print/raster \ -H 'Content-Type: application/json' \ -d '{ "width":384, "height":200, "density":"中等", "encoding":"base64_msb_1bpp", "data":"" }' ``` ### Image print (generate with prompt, then print) ```bash curl -X POST http:///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) ```bash curl -X POST 'http:///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 ```bash curl -X POST http:///v1/print/text \ -H 'Content-Type: application/json' \ -d '{ "text":"欢迎使用TQ打印机\\n订单号: A1024\\n谢谢惠顾", "density":"中等", "scale":2, "line_spacing":2, "max_height":1800 }' ``` ### Jobs ```bash curl http:///v1/jobs curl http:///v1/jobs/1 ``` ### Voice tap2talk Start session: ```bash curl -X POST http:///v1/voice/session/start ``` Start one tap2talk round: ```bash curl -X POST http:///v1/voice/tap/start ``` Cancel current tap2talk round: ```bash curl -X POST http:///v1/voice/tap/cancel ``` Get voice status: ```bash curl http:///v1/voice/status ``` Stop session: ```bash curl -X POST http:///v1/voice/session/stop ``` ## Notes - BLE side is central/client role, not printer peripheral role. - `base64_msb_1bpp` uses Android-compatible bit order (MSB first). - `/v1/print/image` supports two modes: JSON prompt generation (DashScope Z-Image) and raw `image/png` upload (no base64 wrapper). - `/v1/print/text` supports 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.csv` with a 4MB `factory` app partition on 16MB flash modules. - Voice flow is now single-round: audio uplink stops after `SpeechEnded`, then `LocalRespondingEnded` and `Stop` are 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.bin` - `components/domain/assets/fonts/cn16_glyphs.bin` Regenerate from your own CJK font: ```bash 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 ```