Files
AI_Printer/README.md
2026-02-24 18:51:53 +08:00

276 lines
6.8 KiB
Markdown

# ESP32-S3 LYF 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 (`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 web UI at `/` for connect/status, text print, and image upload print
- 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)
- Android-compatible print flow:
- `0x00` power on
- `0x03` set print param
- `0x04` chunked raster send + ACK
- `0x00` power off
- `0x02` feed paper
- REST APIs:
- 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` (direct JPEG binary upload, no base64)
- `POST /v1/print/qr`
- `POST /v1/print/text`
- `POST /v1/print/receipt`
- `POST /v1/print/label`
- Jobs:
- `GET /v1/jobs`
- `DELETE /v1/jobs`
- `GET /v1/jobs/{id}`
- `DELETE /v1/jobs/{id}`
- Label control:
- `POST /v1/label/gap_move`
- `GET /v1/label/offset`
- `POST /v1/label/offset`
- OTA:
- `GET /v1/ota/version`
- `POST /v1/ota/jump_boot`
- `POST /v1/ota/jump_app`
- `POST /v1/ota/erase_page`
- `POST /v1/ota/write_frame`
- `POST /v1/ota/upgrade`
## 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 -> LYF Controller Config`:
- `LYF_WIFI_SSID`
- `LYF_WIFI_PASSWORD`
- `LYF_HTTP_PORT`
- `LYF_API_KEY` (optional)
When `LYF_API_KEY` is not empty, each request must include:
```text
X-API-Key: <your-key>
```
STA-only behavior:
- If SSID is empty, startup fails.
- If STA connect fails, startup fails (no AP fallback).
## Flash
```bash
cd ai_printer
idf.py -p /dev/tty.usbmodemXXXX flash monitor
```
## Web UI
Open the controller IP in a browser:
```text
http://<esp-ip>/
```
The page provides:
- health/status check
- connect/disconnect printer
- submit a simple text print job
- upload a JPG image (binary payload) and let ESP32-S3 decode/threshold/scale/raster conversion
- view jobs list
If API key is enabled, input it in the page before invoking actions.
## REST Examples
### Health
```bash
curl http://<esp-ip>/v1/health
```
### Connect printer
```bash
curl -X POST http://<esp-ip>/v1/printer/connect \
-H 'Content-Type: application/json' \
-d '{"name":"lyfPrinter","timeout_ms":15000}'
```
Auto-match a compatible printer (by BLE service `0xFFF0`):
```bash
curl -X POST http://<esp-ip>/v1/printer/connect \
-H 'Content-Type: application/json' \
-d '{"name":"*","timeout_ms":20000}'
```
### Raster print
```bash
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 (direct JPG upload, no base64)
```bash
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/jpeg' \
--data-binary @./sample.jpg
```
Request constraints:
- Request body limit: 4 MB
- Suggested image size: <= 3 MB when using built-in Web UI uploader
### QR print
```bash
curl -X POST http://<esp-ip>/v1/print/qr \
-H 'Content-Type: application/json' \
-d '{
"text":"https://example.com/pay/123",
"ecc":"H",
"module_scale":0,
"margin_modules":2,
"density":"中等"
}'
```
### Text print
```bash
curl -X POST http://<esp-ip>/v1/print/text \
-H 'Content-Type: application/json' \
-d '{
"text":"欢迎使用LYF打印机\\n订单号: A1024\\n谢谢惠顾",
"density":"中等",
"scale":2,
"line_spacing":2,
"max_height":1800
}'
```
### Receipt print
```bash
curl -X POST http://<esp-ip>/v1/print/receipt \
-H 'Content-Type: application/json' \
-d '{
"title":"LYF FOOD",
"density":"中等",
"footer":"Thanks!",
"items":[
{"name":"DishA","qty":2,"price":12.5},
{"name":"DishB","qty":1,"price":8.0}
]
}'
```
### Label print (with optional gap/offset)
```bash
curl -X POST http://<esp-ip>/v1/print/label \
-H 'Content-Type: application/json' \
-d '{
"width":384,
"height":260,
"encoding":"base64_msb_1bpp",
"data":"<BASE64_BYTES>",
"gap_move_before":true,
"offset_tenths_mm":128,
"density":"中等"
}'
```
### Jobs
```bash
curl http://<esp-ip>/v1/jobs
curl http://<esp-ip>/v1/jobs/1
curl -X DELETE http://<esp-ip>/v1/jobs/1
curl -X DELETE http://<esp-ip>/v1/jobs -d '{"include_success":true,"include_failed":true,"include_canceled":true}'
```
### Label control
```bash
curl -X POST http://<esp-ip>/v1/label/gap_move
curl http://<esp-ip>/v1/label/offset
curl -X POST http://<esp-ip>/v1/label/offset \
-H 'Content-Type: application/json' \
-d '{"offset_tenths_mm":128}'
```
### OTA primitives
```bash
curl http://<esp-ip>/v1/ota/version
curl -X POST http://<esp-ip>/v1/ota/jump_boot
curl -X POST http://<esp-ip>/v1/ota/erase_page -H 'Content-Type: application/json' -d '{"page_num":0}'
curl -X POST http://<esp-ip>/v1/ota/write_frame -H 'Content-Type: application/json' -d '{"packet_num":0,"is_last_frame":false,"data":"<BASE64_<=236_BYTES>"}'
curl -X POST http://<esp-ip>/v1/ota/jump_app
```
### OTA full upgrade
```bash
curl -X POST http://<esp-ip>/v1/ota/upgrade \
-H 'Content-Type: application/json' \
-d '{
"firmware":"<BASE64_FIRMWARE>",
"jump_boot":true,
"jump_app":true,
"page_size":1024,
"packet_size":236,
"timeout_ms_per_step":5000
}'
```
## Notes
- BLE side is central/client role, not printer peripheral role.
- `base64_msb_1bpp` uses Android-compatible bit order (MSB first).
- `/v1/print/image` accepts raw `image/jpeg` bytes directly (no base64 wrapper).
- Large buffers for image upload/decode prefer PSRAM first, then fallback to internal RAM.
- `/v1/print/text` and `/v1/print/receipt` now support UTF-8 Chinese via embedded 16x16 GB2312 glyphs.
- Characters outside embedded glyph set are rendered as square fallback boxes.
- QR encoding uses embedded `qrcodegen` (Project Nayuki C implementation).
## Font Assets
Embedded files:
- `main/domain/assets/fonts/cn16_index.bin`
- `main/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
```