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:
19
CHANGELOG.md
Normal file
19
CHANGELOG.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
- Fix PT-P710BT/PT-P900W print completion flow and protocol bytes.
|
||||||
|
- Correct `G` raster transfer header length (`n1/n2`) from per-model payload size.
|
||||||
|
- Fix `ESC i z` page index (`n9`) and adjust print-information flags/media kind handling.
|
||||||
|
- Keep `ESC i A` disabled on 128-dot family and split `ESC i K` by head width.
|
||||||
|
- Use `0x0C -> 0x1A` end sequence on 560-dot family to complete feed/cut reliably.
|
||||||
|
- Improve status handling around print completion.
|
||||||
|
- Retry status reads with backoff and safer command ordering.
|
||||||
|
- Add verbose post-print short polling until print-end status is observed.
|
||||||
|
- Add small wait before `--status` command retrieval.
|
||||||
|
- Add protocol regression safeguards.
|
||||||
|
- Introduce `libptouch_protocol` helpers for shared command-byte generation.
|
||||||
|
- Add `protocol_regression_test` and wire it into CTest.
|
||||||
|
- Update documentation.
|
||||||
|
- Add `ctest` step to build instructions.
|
||||||
|
- Link to changelog from README.
|
||||||
@@ -120,4 +120,8 @@ sudo usermod -aG plugdev "$USER"
|
|||||||
|
|
||||||
## ライセンス
|
## ライセンス
|
||||||
|
|
||||||
[MIT License](LICENSE)(`LICENSE` ファイルを参照)。
|
[MIT License](LICENSE)(`LICENSE` ファイルを参照)。
|
||||||
|
|
||||||
|
## 変更履歴
|
||||||
|
|
||||||
|
リリース間の変更点は `CHANGELOG.md` を参照してください。
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
static void usage(const char *argv0)
|
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)
|
if (!ctx)
|
||||||
return;
|
return;
|
||||||
printf("=== verbose: post-print status (%s) ===\n", label);
|
printf("=== verbose: post-print status (%s) ===\n", label);
|
||||||
uint8_t st[LIBPTOUCH_STATUS_LENGTH];
|
/*
|
||||||
if (libptouch_get_status(ctx, st) == LIBPTOUCH_OK) {
|
* 印刷直後は phase change (status[18] != 0x00) が続くことがあるため、
|
||||||
libptouch_status_fprint(stdout, st);
|
* 短時間ポーリングで最終状態まで追跡する。
|
||||||
} else {
|
*/
|
||||||
printf("status: unavailable (%s)\n", libptouch_strerror(ctx));
|
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)
|
int main(int argc, char **argv)
|
||||||
@@ -261,6 +278,10 @@ int main(int argc, char **argv)
|
|||||||
libptouch_destroy(sctx);
|
libptouch_destroy(sctx);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
/* 印刷直後などは内部処理中で最初のステータス応答が不安定なことがあるため、
|
||||||
|
* 少し待ってからステータスを取りに行く。 */
|
||||||
|
usleep(300000); /* 300ms */
|
||||||
|
|
||||||
uint8_t st[LIBPTOUCH_STATUS_LENGTH];
|
uint8_t st[LIBPTOUCH_STATUS_LENGTH];
|
||||||
se = libptouch_get_status(sctx, st);
|
se = libptouch_get_status(sctx, st);
|
||||||
if (se != LIBPTOUCH_OK) {
|
if (se != LIBPTOUCH_OK) {
|
||||||
|
|||||||
@@ -171,8 +171,7 @@ libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
|
|||||||
pos += sizeof(esc_ia);
|
pos += sizeof(esc_ia);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Chain printing off; safer default for 128-dot family as well. */
|
uint8_t esc_ik[] = { 0x1B, 0x69, 0x4B, ptouch_esc_ik_value(head_dots) };
|
||||||
static const uint8_t esc_ik[] = { 0x1B, 0x69, 0x4B, 0x08 };
|
|
||||||
memcpy(head + pos, esc_ik, sizeof(esc_ik));
|
memcpy(head + pos, esc_ik, sizeof(esc_ik));
|
||||||
pos += sizeof(esc_ik);
|
pos += sizeof(esc_ik);
|
||||||
|
|
||||||
@@ -218,6 +217,21 @@ libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
|
|||||||
}
|
}
|
||||||
free(gbuf);
|
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 };
|
static const uint8_t print_end[] = { 0x1A };
|
||||||
v = ptouch_bulk_send_job(ctx, print_end, sizeof(print_end), "print end");
|
v = ptouch_bulk_send_job(ctx, print_end, sizeof(print_end), "print end");
|
||||||
if (v != LIBPTOUCH_OK) {
|
if (v != LIBPTOUCH_OK) {
|
||||||
|
|||||||
@@ -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,
|
void ptouch_fill_esc_iz(uint8_t out[13], uint8_t media_kind, uint8_t media_width,
|
||||||
uint32_t raster_lines)
|
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[0] = 0x1Bu;
|
||||||
out[1] = 0x69u;
|
out[1] = 0x69u;
|
||||||
out[2] = 0x7Au;
|
out[2] = 0x7Au;
|
||||||
out[3] = 0x0Eu;
|
/* PI flags: enable media/width/length with quality bit for broad compatibility. */
|
||||||
out[4] = n2_paper;
|
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[5] = media_width;
|
||||||
out[6] = 0x00u;
|
out[6] = 0x00u;
|
||||||
out[7] = (uint8_t)(raster_lines & 0xFFu);
|
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[11] = 0x00u; /* first page */
|
||||||
out[12] = 0x00u; /* fixed */
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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_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,
|
void ptouch_fill_esc_iz(uint8_t out[13], uint8_t media_kind, uint8_t media_width,
|
||||||
uint32_t raster_lines);
|
uint32_t raster_lines);
|
||||||
|
uint8_t ptouch_esc_ik_value(unsigned head_dots);
|
||||||
|
|
||||||
#endif /* LIBPTOUCH_PROTOCOL_H */
|
#endif /* LIBPTOUCH_PROTOCOL_H */
|
||||||
|
|||||||
@@ -36,13 +36,21 @@ libptouch_err_t libptouch_get_status(libptouch_ctx *ctx, uint8_t *status)
|
|||||||
if (attempt > 0) {
|
if (attempt > 0) {
|
||||||
usleep(120000); /* 120ms backoff */
|
usleep(120000); /* 120ms backoff */
|
||||||
}
|
}
|
||||||
r = ptouch_bulk_out(h, ctx->bulk_out_ep, init, 2, 5000u);
|
/*
|
||||||
if (r != 0) {
|
* Avoid unconditional ESC @ here:
|
||||||
if (r == LIBUSB_ERROR_IO || r == LIBUSB_ERROR_PIPE ||
|
* some models can be in phase-change right after print end, and
|
||||||
r == LIBUSB_ERROR_TIMEOUT)
|
* re-initializing there may disturb cutter/feed completion.
|
||||||
continue;
|
* Try plain ESC i S first, use ESC @ only on retry paths.
|
||||||
ptouch_set_error_usb(ctx, r, "bulk OUT ESC @");
|
*/
|
||||||
return LIBPTOUCH_ERR_USB;
|
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);
|
r = ptouch_bulk_out(h, ctx->bulk_out_ep, req, 3, 5000u);
|
||||||
if (r != 0) {
|
if (r != 0) {
|
||||||
|
|||||||
@@ -34,11 +34,16 @@ int main(void)
|
|||||||
fail |= expect_int("iz_cmd_0", iz[0], 0x1B);
|
fail |= expect_int("iz_cmd_0", iz[0], 0x1B);
|
||||||
fail |= expect_int("iz_cmd_1", iz[1], 0x69);
|
fail |= expect_int("iz_cmd_1", iz[1], 0x69);
|
||||||
fail |= expect_int("iz_cmd_2", iz[2], 0x7A);
|
fail |= expect_int("iz_cmd_2", iz[2], 0x7A);
|
||||||
fail |= expect_int("iz_media_kind_map", iz[4], 0x09);
|
fail |= expect_int("iz_n1_flags", iz[3], 0x8E);
|
||||||
|
fail |= expect_int("iz_media_kind_passthrough", iz[4], 0x01);
|
||||||
fail |= expect_int("iz_media_width", iz[5], 0x0C);
|
fail |= expect_int("iz_media_width", iz[5], 0x0C);
|
||||||
fail |= expect_int("iz_lines_lsb", iz[7], 70);
|
fail |= expect_int("iz_lines_lsb", iz[7], 70);
|
||||||
fail |= expect_int("iz_page_index", iz[11], 0x00);
|
fail |= expect_int("iz_page_index", iz[11], 0x00);
|
||||||
fail |= expect_int("iz_last_fixed", iz[12], 0x00);
|
fail |= expect_int("iz_last_fixed", iz[12], 0x00);
|
||||||
|
|
||||||
|
/* ESC i K mode byte: 128-dot vs 560-dot families */
|
||||||
|
fail |= expect_int("esc_ik_128", ptouch_esc_ik_value(128u), 0x08);
|
||||||
|
fail |= expect_int("esc_ik_560", ptouch_esc_ik_value(560u), 0x0C);
|
||||||
|
|
||||||
return fail ? EXIT_FAILURE : EXIT_SUCCESS;
|
return fail ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user