add POS895/POS896 feasibility notes and CLAUDE.md pointer
Study-only writeup of what supporting the SPRT-derived POS895/POS896 ESC/POS clones would take: no vendor SDK linking, a 576-dot IPrinter adapter, non-ENPC discovery, a sturdier printer-ID probe, and the hardware checks still open. Claude-Session: https://claude.ai/code/session_01WAG8xogHxq9hDJScu386Jf
This commit is contained in:
@@ -180,6 +180,10 @@ Printers are registered via `PreconfiguredDiscoveryService` with hardcoded IPs i
|
|||||||
5. Add receipt converter support in `ReceiptConverterFactory` if needed
|
5. Add receipt converter support in `ReceiptConverterFactory` if needed
|
||||||
6. Test with actual hardware - different models have different image widths and positioning
|
6. Test with actual hardware - different models have different image widths and positioning
|
||||||
|
|
||||||
|
### Non-Epson printers (POS895 / POS896)
|
||||||
|
|
||||||
|
A feasibility study for the POS895 / POS896 ESC/POS clones lives in `POS895_POS896_integration_notes.md`. Read it before touching discovery, printer identification (`GetPrinterIdAsync`), or `PrinterWrapperFactory` for a non-Epson model. Short version: do not link the vendor SDK; add an `IPrinter` adapter for a 576-dot line, a discovery strategy that is not Epson ENPC, and a more robust printer-ID probe. The document lists the ESC/POS commands the vendor firmware is known to accept and the hardware checks still open.
|
||||||
|
|
||||||
## Adding New Receipt Types
|
## Adding New Receipt Types
|
||||||
|
|
||||||
1. Create a new folder under `Inspectron.Epson/PrintServer/Printers/Utils/`
|
1. Create a new folder under `Inspectron.Epson/PrintServer/Printers/Utils/`
|
||||||
|
|||||||
135
POS895_POS896_integration_notes.md
Normal file
135
POS895_POS896_integration_notes.md
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# POS895 / POS896 printer support: feasibility notes
|
||||||
|
|
||||||
|
Date: 2026-09-02. Status: study only, no code changes made.
|
||||||
|
|
||||||
|
Source material reviewed: `D:\Inspectron\James\epson\POS895\POS895&896 SDK\` (Linux SDK, Windows POS_SDK, EasySet tool, POS895 and POS896 user manuals, POS_SDK Development Manual V1.4).
|
||||||
|
|
||||||
|
## Verdict
|
||||||
|
|
||||||
|
The POS895 and POS896 can be supported by this codebase, but **not by linking the vendor SDK**. The SDK is a thin C wrapper that builds ESC/POS byte strings and writes them to a raw TCP socket on port 9100. `Inspectron.Epson/EpsonPrinter.cs` already does the same thing in C#. The real work is:
|
||||||
|
|
||||||
|
1. a new `IPrinter` adapter for a 576-dot, 80 mm ESC/POS clone,
|
||||||
|
2. a discovery strategy that does not rely on Epson's ENPC broadcast,
|
||||||
|
3. a more robust printer identification than the single `GS I 1` byte,
|
||||||
|
4. a hardware probe session to close the unknowns listed below.
|
||||||
|
|
||||||
|
Rough effort: 3 to 5 developer days once a printer is on the bench. No new deployment dependencies, no native code, no libusb. Restrict to the Ethernet models (POS895 UEd, POS896 UE); USB-attached operation is a separate project because the whole pipeline keys on an IP address.
|
||||||
|
|
||||||
|
## What the vendor SDK is
|
||||||
|
|
||||||
|
Path: `Linux_SDK\Linux_SDK_20190110\Linux_SDK_64_group_ARM\Linux_SDK_64_group\`
|
||||||
|
|
||||||
|
| File | What it is |
|
||||||
|
|---|---|
|
||||||
|
| `HWISPRTPrinter.c` / `.h` | ~5,500 lines of C. Transports: libusb, serial tty, TCP socket to port 9100. Every `POS_*` function fills a global 1 KB buffer with an ESC/POS sequence and writes it. |
|
||||||
|
| `sprtGraphic.c` / `.h` | BMP loader and a proprietary line-bitmap encoder (see below). |
|
||||||
|
| `libHWISPRTPrinter.so` | Prebuilt library. **32-bit Intel i386**, despite the folder name saying ARM. |
|
||||||
|
| `libusb/` | Bundled libusb, **x86-64**. |
|
||||||
|
| `testMain.c`, `CPCLtest.c`, `TSPLtest.c`, `CPCLDemo.c`, `GtestMain.c` | Demos. Only `testMain.c` is ESC/POS. |
|
||||||
|
| `makefile` | Cross-compiles with `arm-linux-gcc`. The shipped binaries were not built with it. |
|
||||||
|
| `README.txt` | Chinese, GBK encoded. Decode with `iconv -f GB18030 -t UTF-8`. The `.c` and `.h` files are UTF-8. |
|
||||||
|
|
||||||
|
Reasons not to use it:
|
||||||
|
|
||||||
|
- Global state only. One socket descriptor (`socketFd`), one command buffer (`gCmdBuf`), no printer handle. It cannot drive two printers concurrently; `PrintServer` does.
|
||||||
|
- The shipped `.so` would not load on the Raspberry Pi. It would need cross-compiling from source, then P/Invoke from .NET, for zero functional gain over what `EpsonPrinter` already sends.
|
||||||
|
- Roughly half the API is CPCL and TSPL label-printer commands. Irrelevant for receipts.
|
||||||
|
- `pos-sdk\POS-SDK\POS-SDK_V1219\` is a Windows `POS_SDK.dll` with a WinForms P/Invoke demo (`Testing\C#\TestSDKCSharp\Form1.cs`). Irrelevant for the Linux service. Its manual (`POS_SDK Development Manual V1.4_Neutral.pdf`) is in English and is the best command reference in the package.
|
||||||
|
- Development stopped around 2016 to 2019 for the Linux part.
|
||||||
|
|
||||||
|
The firmware family is "SPRT" (the SDK is named `HWISPRTPrinter`, log prefix `qzf`). Treat the printer as a SPRT-derived ESC/POS clone.
|
||||||
|
|
||||||
|
## Printer facts from the manuals
|
||||||
|
|
||||||
|
| | POS895 | POS896 |
|
||||||
|
|---|---|---|
|
||||||
|
| Paper | 80 mm (79.5 ±0.5), 72 mm printable | same |
|
||||||
|
| Resolution | 203 dpi, 8 dots/mm, 576 dots/line | same |
|
||||||
|
| Fonts | Font A 12x24 (48 columns), Font B 9x17, CJK 24x24 | same |
|
||||||
|
| Interfaces | Ud: USB. UEd: USB + Ethernet | UBTDM/UBTMM: USB + Bluetooth + drawer. UE: USB + serial + Ethernet + drawer |
|
||||||
|
| Command set | ESC/POS | ESC/POS |
|
||||||
|
| Barcodes | UPC/EAN/CODE39/ITF/CODABAR/CODE93/CODE128, PDF417, QR, DataMatrix | same |
|
||||||
|
| Speed | 160 mm/s | 260 mm/s |
|
||||||
|
| DHCP | Off by default, auto-enables if the static IP cannot reach the router | same |
|
||||||
|
| Factory reset | `1B 09 1B 92 FF 01` | |
|
||||||
|
| Hex dump mode | Open cover, press FEED twice within 3 s, close cover. Exit: open cover, press FEED three times. | |
|
||||||
|
| Self-test | Press FEED twice within 3 s. Prints model, firmware, interface, default code page. | |
|
||||||
|
|
||||||
|
The POS896 buffers up to 45 KB of unprinted data across a paper-out and prints it after reloading. Worth knowing when reasoning about retries.
|
||||||
|
|
||||||
|
## ESC/POS commands the SDK emits (so the firmware certainly supports them)
|
||||||
|
|
||||||
|
| Function | Bytes |
|
||||||
|
|---|---|
|
||||||
|
| Initialize | `1B 40` |
|
||||||
|
| Feed n lines | `1B 64 n` |
|
||||||
|
| Cut, full / partial | `1D 56 00` / `1D 56 01` (SDK always feeds 3 lines first) |
|
||||||
|
| Line spacing | `1B 33 n` |
|
||||||
|
| Alignment | `1B 61 n` |
|
||||||
|
| Left margin | `1D 4C nL nH` |
|
||||||
|
| Font mode (font B, bold, double width, double height, underline) | `1B 21 n` (bits 0, 3, 5, 4, 7) |
|
||||||
|
| Bit image | `1B 2A m nL nH data`, m in {0, 1, 32, 33} |
|
||||||
|
| Real-time status | `10 04 n`, n in 1..4, one-byte reply |
|
||||||
|
| Printer ID | `1D 49 42` (GS I 66, returns `"_" + factory name` as a string) |
|
||||||
|
| Cash drawer | `1B 70 m t1 t2` |
|
||||||
|
| Inverse print | `1D 42 n` |
|
||||||
|
| Rotate / upside-down | `1B 56 n` / `1B 7B n` |
|
||||||
|
| 1D barcode | `1D 6B ...` |
|
||||||
|
| 2D barcode | `1D 5A ...` (PDF417 / DataMatrix / QR selector) |
|
||||||
|
| Proprietary line bitmap | `1D 44 01`, then per non-blank row `15 <skip> 16 <bytes> <inverted row data>`, then `1D 44 00` |
|
||||||
|
|
||||||
|
Status byte interpretation in the SDK (matches Epson's DLE EOT layout and our parsers in `EpsonPrinterStatus.cs`):
|
||||||
|
|
||||||
|
| Query | Fixed bits | Meaning of set bit |
|
||||||
|
|---|---|---|
|
||||||
|
| `10 04 01` printer status | `0x12` must be set | `0x04` drawer high |
|
||||||
|
| `10 04 02` offline status | `0x12` | `0x04` cover open |
|
||||||
|
| `10 04 03` error status | `0x12` | `0x08` cutter error, `0x20` unrecoverable, `0x40` auto-recoverable |
|
||||||
|
| `10 04 04` paper status | `0x12` | `0x40` paper end |
|
||||||
|
|
||||||
|
Code pages: the SDK manual's code page table (TSPL section, but the same family) has index 18 = CP852, matching Epson numbering. Our `EpsonCommands.CP852Encoding` sends `1B 74 12`, so Latin-2 text is probably fine. Confirm on the self-test page.
|
||||||
|
|
||||||
|
## Compatibility of what `TM_T30IIITranslated` sends today
|
||||||
|
|
||||||
|
| Command | Our use | Assessment |
|
||||||
|
|---|---|---|
|
||||||
|
| ESC @, ESC d, GS V, ESC 3, ESC 2 | init, feed, cut, line spacing | Fine, SDK emits them. |
|
||||||
|
| DLE EOT 1..4 | status | Fine, identical layout. |
|
||||||
|
| GS ! n (`SetFontSizeAsync`) | magnification | Likely fine, Windows SDK exposes a width/height font-size call. |
|
||||||
|
| ESC E (`SetEmphasized`) | bold | Standard, likely fine. SDK uses ESC ! bit 3 instead. |
|
||||||
|
| ESC t 18 + CP852 | umlauts | Likely fine, see code pages above. |
|
||||||
|
| ESC $ (`SetAbsolutePrintPosition`) | logo offset 100 | Unverified. SDK uses GS L margins instead. |
|
||||||
|
| GS ( L fn 112 / fn 50 (`LoadImageAsync` + `PrintLoadedImage`) | logo | Probably unsupported. SDK never uses it. Fallbacks: ESC * 24-dot double density (`PrintImageBitModeAsync`, already implemented) or GS v 0 (not implemented). |
|
||||||
|
| GS I 1 (`GetPrinterIdAsync`) | model byte | **Unknown.** SDK uses GS I 66 for a maker string instead. |
|
||||||
|
| ESC r (`SetRedColor`) | two-colour | Not applicable, monochrome printer. The T30III adapter ignores `IsRed` anyway. |
|
||||||
|
|
||||||
|
## What breaks today if a POS895 is plugged into the network
|
||||||
|
|
||||||
|
1. **Discovery.** `PrintServerBootstrapper` binds `DiscoveryService`, which broadcasts Epson's proprietary `EPSONQ` packet on UDP 3289. The POS895 never answers. `PrintServer.RegisterPrinter` is never called, so `SubmitJob` logs "Printer not found" and reports the job Failed. `JamesDiscoveredPrintersReceiver` never tells the backend about the printer, so it cannot be assigned to a work area.
|
||||||
|
2. **Identification.** `EpsonPrinter.GetPrinterIdAsync` sends `1D 49 01` and reads one byte. Three places dispatch on that byte: `PrinterWrapperFactory.CreatePrinterFromId`, `ReceiptConverterFactory.Create` (48 vs 33 columns), and `DiscoveryService.GetModelNameFromId`. If the printer answers nothing, the read waits the full connection timeout (10 s), returns null, `printerId.Value` throws, the job fails with `PrintErrorType.Other`, and `PrinterQueue` retries three times. If it answers `0x01` it is silently treated as a TM-T30III and mostly works by accident, with a 384 px logo at offset 100 on a 576-dot line.
|
||||||
|
3. **Paper-out logic.** `EpsonPrintService.PrintAsync` maps `!PrinterStatus.IsOnline` (DLE EOT 1 bit 3) to `OutOfPaper`. Whether this firmware sets that bit on paper-out is unverified. DLE EOT 4 bit 6 is what the SDK uses.
|
||||||
|
|
||||||
|
## Work items
|
||||||
|
|
||||||
|
1. **Hardware probe (do first, half a day).** With the printer in hex dump mode and then in normal mode, send and record replies for:
|
||||||
|
- `1D 49 01`, `1D 49 41`, `1D 49 42`, `1D 49 43` (what identifies the model, and how many bytes come back)
|
||||||
|
- `10 04 01..04` in normal, paper-out, cover-open states
|
||||||
|
- `1B 74 12` followed by `ä ö ü ß č` bytes in CP852
|
||||||
|
- `1D 21 00 / 11 / 10 / 01` text sizes
|
||||||
|
- `1B 24 nL nH` then text (does absolute positioning work)
|
||||||
|
- a logo via GS ( L, via ESC * 33, and via `1D 76 30`
|
||||||
|
- `EpsonDiagnostics.RunFullDiagnosticsAsync` output for the record
|
||||||
|
2. **Adapter.** New `Inspectron.Epson/PrintServer/Printers/POS895.cs` implementing `IPrinter`, cloned from `TM_T30IIITranslated`: logo width 576, centred offset `(576 - width) / 2`, image via ESC * or GS v 0, delays tuned. One case each in `PrinterWrapperFactory`, `ReceiptConverterFactory` (48 columns), `DiscoveryService.GetModelNameFromId`.
|
||||||
|
3. **Identification hardening in `EpsonPrinter`.** Shorter read timeout for GS I, fall back to the GS I 67 model-name string, or to a per-IP configured model, when the byte is missing or collides with an Epson ID.
|
||||||
|
4. **Discovery for non-Epson printers.** Preferred: sweep the local /24 for open TCP 9100 and run the identification probe on responders. Alternatives: the vendor's UDP discovery protocol used by `EasySet Tool.exe` (unknown, needs a packet capture; the exe yields no readable strings), or manual static registration through `SimpleHttpServer`. Then a composite `IDiscoveryService` that merges ENPC results with the new strategy. `PrinterDiscoveryBackgroundTask` needs no change. This is the largest item, 1 to 2 days.
|
||||||
|
5. **Emulation and tests.** Add a 576 px `HtmlPrinter` entry with the new ID to the emulation dictionary in `PrintServerBootstrapper`, and EpsonTest cases, so receipt layouts can be checked without hardware.
|
||||||
|
6. **GS v 0 raster** in `EpsonCommands`, only if the probe shows GS ( L is rejected.
|
||||||
|
7. **CLAUDE.md updates** once the adapter exists: printer ID byte, image width, discovery method.
|
||||||
|
|
||||||
|
## Open questions that only hardware can answer
|
||||||
|
|
||||||
|
- What does `GS I 1` return, if anything?
|
||||||
|
- Does the firmware accept `GS ( L` raster graphics?
|
||||||
|
- Is code page 18 really CP852 in ESC/POS mode?
|
||||||
|
- Does DLE EOT 1 bit 3 go high on paper-out?
|
||||||
|
- Does the printer answer any UDP or mDNS discovery at all?
|
||||||
Reference in New Issue
Block a user