diff --git a/packages/core/src/device/profiles/p12.ts b/packages/core/src/device/profiles/p12.ts index e865543..b6a626c 100644 --- a/packages/core/src/device/profiles/p12.ts +++ b/packages/core/src/device/profiles/p12.ts @@ -37,6 +37,7 @@ export const p12Profile: DeviceProfile = { packetSize: 90, flowControl: { initialCredits: 4, + packetDelayMs: 30, }, defaults: { density: 2, paperType: "gap" }, namePrefixes: ["P12", "LP90", "P11"], diff --git a/packages/core/src/device/profiles/p15.ts b/packages/core/src/device/profiles/p15.ts index ea62c37..b93fa28 100644 --- a/packages/core/src/device/profiles/p15.ts +++ b/packages/core/src/device/profiles/p15.ts @@ -25,6 +25,7 @@ export const p15Profile: DeviceProfile = { packetSize: 95, flowControl: { initialCredits: 4, + packetDelayMs: 30, }, defaults: { density: 2, paperType: "gap" }, namePrefixes: [ diff --git a/packages/core/src/printer.ts b/packages/core/src/printer.ts index 6e8e93e..5080663 100644 --- a/packages/core/src/printer.ts +++ b/packages/core/src/printer.ts @@ -244,8 +244,14 @@ export class Printer { } else if (response.type === "mtu") { const mtu = response.value as number; if (mtu > 3) { - debugLog("BLE", `mtu=${mtu} packet=${mtu - 3}`); - this.flowController.setPacketSize(mtu - 3); + const mtuPacketSize = mtu - 3; + // Cap at profile-specified size — the profile knows what the device + // firmware can handle; a higher BLE MTU doesn't mean the printer's + // application-level buffer can keep up with larger writes. + const cap = this.profile.packetSize; + const effective = cap ? Math.min(mtuPacketSize, cap) : mtuPacketSize; + debugLog("BLE", `mtu=${mtu} packet=${effective}${cap && mtuPacketSize > cap ? ` (capped from ${mtuPacketSize})` : ""}`); + this.flowController.setPacketSize(effective); } } diff --git a/packages/core/src/transport/flow-control.ts b/packages/core/src/transport/flow-control.ts index 4b5ffe0..8698bea 100644 --- a/packages/core/src/transport/flow-control.ts +++ b/packages/core/src/transport/flow-control.ts @@ -5,7 +5,7 @@ import { debugLog } from "../debug-log.js"; const DEFAULT_OPTIONS: FlowControlOptions = { initialCredits: 4, - starvationTimeoutMs: 30, + starvationTimeoutMs: 1000, timerIntervalMs: 5, packetDelayMs: 0, }; @@ -35,7 +35,10 @@ export class FlowController { /** Reset credits to initial state before a new print job. */ reset(): void { this.credits = this.options.initialCredits; - this.hasRealCredits = false; + // Preserve hasRealCredits — it reflects whether this device supports + // credit-based flow control, which is a connection-level property. + // Clearing it would let starvation recovery bypass flow control at the + // start of every print job. this.lastCreditTime = Date.now(); debugLog("FC", `reset, credits=${this.credits}`); } @@ -61,9 +64,15 @@ export class FlowController { let offset = 0; debugLog("TX", `sending ${data.length}B in ${Math.ceil(data.length / this.packetSize)} packets, credits=${this.credits}`); + const { packetDelayMs } = this.options; + while (offset < data.length) { await this.waitForCredit(); + if (packetDelayMs > 0 && offset > 0) { + await new Promise((r) => setTimeout(r, packetDelayMs)); + } + const remaining = data.length - offset; const chunkSize = Math.min(remaining, this.packetSize); const chunk = data.subarray(offset, offset + chunkSize);