Fix print completion flow for PT-P710BT and PT-P900W.

Align raster protocol bytes and print-end sequencing by printer family, add safer status polling/retry behavior, and document the changes with regression coverage to prevent protocol regressions.

Made-with: Cursor
This commit is contained in:
knb
2026-04-17 05:17:22 +09:00
parent 779a50747d
commit d2fd6cc1f9
8 changed files with 98 additions and 28 deletions

View File

@@ -13,6 +13,7 @@
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
static void usage(const char *argv0)
{
@@ -141,12 +142,28 @@ static void verbose_print_post_print_status(libptouch_ctx *ctx, const char *labe
if (!ctx)
return;
printf("=== verbose: post-print status (%s) ===\n", label);
uint8_t st[LIBPTOUCH_STATUS_LENGTH];
if (libptouch_get_status(ctx, st) == LIBPTOUCH_OK) {
libptouch_status_fprint(stdout, st);
} else {
printf("status: unavailable (%s)\n", libptouch_strerror(ctx));
/*
* 印刷直後は phase change (status[18] != 0x00) が続くことがあるため、
* 短時間ポーリングで最終状態まで追跡する。
*/
const int max_polls = 12; /* ~2.4s */
const useconds_t poll_interval_us = 200000;
for (int i = 0; i < max_polls; i++) {
uint8_t st[LIBPTOUCH_STATUS_LENGTH];
if (libptouch_get_status(ctx, st) != LIBPTOUCH_OK) {
printf("status poll %d/%d: unavailable (%s)\n", i + 1,
max_polls, libptouch_strerror(ctx));
} else {
printf("status poll %d/%d:\n", i + 1, max_polls);
libptouch_status_fprint(stdout, st);
if (st[18] == 0x00u) {
printf("post-print polling settled: print end.\n");
return;
}
}
usleep(poll_interval_us);
}
printf("post-print polling timeout: phase did not settle.\n");
}
int main(int argc, char **argv)
@@ -261,6 +278,10 @@ int main(int argc, char **argv)
libptouch_destroy(sctx);
return 1;
}
/* 印刷直後などは内部処理中で最初のステータス応答が不安定なことがあるため、
* 少し待ってからステータスを取りに行く。 */
usleep(300000); /* 300ms */
uint8_t st[LIBPTOUCH_STATUS_LENGTH];
se = libptouch_get_status(sctx, st);
if (se != LIBPTOUCH_OK) {

View File

@@ -171,8 +171,7 @@ libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
pos += sizeof(esc_ia);
}
/* Chain printing off; safer default for 128-dot family as well. */
static const uint8_t esc_ik[] = { 0x1B, 0x69, 0x4B, 0x08 };
uint8_t esc_ik[] = { 0x1B, 0x69, 0x4B, ptouch_esc_ik_value(head_dots) };
memcpy(head + pos, esc_ik, sizeof(esc_ik));
pos += sizeof(esc_ik);
@@ -218,6 +217,21 @@ libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
}
free(gbuf);
/*
* Some 560-dot family devices are sensitive to end-of-page sequencing.
* Send FF (0x0C) then Control-Z (0x1A) on that family to explicitly
* terminate page and final feed/cut.
*/
if (head_dots > 128u) {
static const uint8_t page_end[] = { 0x0C };
v = ptouch_bulk_send_job(ctx, page_end, sizeof(page_end),
"print page end");
if (v != LIBPTOUCH_OK) {
free(transposed);
return v;
}
}
static const uint8_t print_end[] = { 0x1A };
v = ptouch_bulk_send_job(ctx, print_end, sizeof(print_end), "print end");
if (v != LIBPTOUCH_OK) {

View File

@@ -15,21 +15,13 @@ void ptouch_fill_gf_header(uint8_t out[3], size_t line_payload_bytes)
void ptouch_fill_esc_iz(uint8_t out[13], uint8_t media_kind, uint8_t media_width,
uint32_t raster_lines)
{
uint8_t n2_paper = 0x09u;
if (media_kind == 0x03u)
n2_paper = 0x00u;
else if (media_kind == 0x11u)
n2_paper = 0x11u;
else if (media_kind == 0x17u)
n2_paper = 0x17u;
else if (media_kind == 0x13u)
n2_paper = 0x13u;
out[0] = 0x1Bu;
out[1] = 0x69u;
out[2] = 0x7Au;
out[3] = 0x0Eu;
out[4] = n2_paper;
/* PI flags: enable media/width/length with quality bit for broad compatibility. */
out[3] = 0x8Eu;
/* Use status media-kind byte directly (e.g., 0x01 laminated, 0x03 non-laminate). */
out[4] = media_kind;
out[5] = media_width;
out[6] = 0x00u;
out[7] = (uint8_t)(raster_lines & 0xFFu);
@@ -39,3 +31,9 @@ void ptouch_fill_esc_iz(uint8_t out[13], uint8_t media_kind, uint8_t media_width
out[11] = 0x00u; /* first page */
out[12] = 0x00u; /* fixed */
}
uint8_t ptouch_esc_ik_value(unsigned head_dots)
{
/* Preserve legacy 560-dot behaviour (0x0C), 128-dot uses 0x08. */
return head_dots > 128u ? 0x0Cu : 0x08u;
}

View File

@@ -8,5 +8,6 @@ size_t ptouch_line_payload_bytes(unsigned head_dots);
void ptouch_fill_gf_header(uint8_t out[3], size_t line_payload_bytes);
void ptouch_fill_esc_iz(uint8_t out[13], uint8_t media_kind, uint8_t media_width,
uint32_t raster_lines);
uint8_t ptouch_esc_ik_value(unsigned head_dots);
#endif /* LIBPTOUCH_PROTOCOL_H */

View File

@@ -36,13 +36,21 @@ libptouch_err_t libptouch_get_status(libptouch_ctx *ctx, uint8_t *status)
if (attempt > 0) {
usleep(120000); /* 120ms backoff */
}
r = ptouch_bulk_out(h, ctx->bulk_out_ep, init, 2, 5000u);
if (r != 0) {
if (r == LIBUSB_ERROR_IO || r == LIBUSB_ERROR_PIPE ||
r == LIBUSB_ERROR_TIMEOUT)
continue;
ptouch_set_error_usb(ctx, r, "bulk OUT ESC @");
return LIBPTOUCH_ERR_USB;
/*
* Avoid unconditional ESC @ here:
* some models can be in phase-change right after print end, and
* re-initializing there may disturb cutter/feed completion.
* Try plain ESC i S first, use ESC @ only on retry paths.
*/
if (attempt > 0) {
r = ptouch_bulk_out(h, ctx->bulk_out_ep, init, 2, 5000u);
if (r != 0) {
if (r == LIBUSB_ERROR_IO || r == LIBUSB_ERROR_PIPE ||
r == LIBUSB_ERROR_TIMEOUT)
continue;
ptouch_set_error_usb(ctx, r, "bulk OUT ESC @");
return LIBPTOUCH_ERR_USB;
}
}
r = ptouch_bulk_out(h, ctx->bulk_out_ep, req, 3, 5000u);
if (r != 0) {