Memperbaiki Struk Printer Termal Bluetooth Flutter yang Kacau

Kalau hanya karakter beraksen yang salah, byte UTF-8 sedang dibaca lewat code page satu byte sehingga tiap byte menjadi glifnya sendiri. Kalau karakter pertama saja sudah berupa sampah, dialek perintahnya yang salah — biasanya capability profile yang tidak cocok dengan perangkat keras, atau urutan byte yang disalin dari model printer lain.
Payload-nya melampaui buffer printer. Printer Bluetooth 58 mm punya buffer kecil dan sering tanpa flow control yang berguna, jadi penulisan panjang bisa kehilangan ekornya begitu saja. Kirim dalam potongan sekitar 128 sampai 256 byte dengan jeda pendek di antaranya, dan setel kedua nilainya memakai struk terpanjang yang benar-benar dicetak toko.
Hampir pasti ukuran kertas yang diberikan ke generator. Membangun untuk 80 mm lalu mencetak di 58 mm tidak terpotong dengan jelas; ia menghasilkan teks yang secara konsisten tidak sejajar di margin kanan karena jumlah kolomnya salah. Periksa argumen PaperSize sebelum yang lain.
Penulisan yang sukses hanya berarti byte meninggalkan perangkat. Penyebab umumnya adalah koneksi basi setelah printer tidur atau sempat dimatikan, atau printer yang sedang tidur dan kehilangan penulisan pertama yang membangunkannya. Sambung ulang dan coba sekali lagi secara otomatis sebelum menampilkan error ke kasir.
Bekerjalah naik dari perangkat keras. Cetak self test milik printer, lalu inisialisasi polos ditambah beberapa karakter dan perintah potong, lalu teks bergaya, lalu byte fixture yang di-commit, baru struk sungguhan. Setiap langkah menambah tepat satu lapisan, jadi langkah pertama yang gagal langsung menyebut pelakunya tanpa menebak.

Ringkasan Utama
Struk Bluetooth yang kacau hampir selalu punya salah satu dari lima penyebab: UTF-8 dikirim ke code page satu byte, payload yang melampaui buffer printer, ukuran kertas salah di generator, capability profile yang tidak cocok dengan perangkat keras, atau koneksi basi. Masing-masing punya gejala berbeda di kertas.
Pencetakan termal gagal dengan cara yang unik tidak membantu: printer menerima segalanya, tidak melaporkan apa pun, dan satu-satunya keluaran diagnostiknya adalah selembar kertas. Namun kertas itu justru pesan error yang sangat presisi begitu Anda belajar membacanya — bentuk kerusakannya memberi tahu lapisan mana yang bermasalah.
Ini debugging berbasis gejala. Temukan baris yang cocok dengan apa yang keluar dari printer, lalu baca bagian yang menjelaskannya.
Sebelum mengubah kode apa pun, amati struk yang gagal dengan teliti dan cocokkan dengan tabel ini. Kecocokannya biasanya tidak ambigu.
| Apa yang terlihat di kertas | Hampir pasti | Periksa di mana |
|---|---|---|
| Tata letak benar, tetapi karakter beraksen jadi dua glif aneh | Byte UTF-8 dibaca lewat code page satu byte | Encoding teks dan code page yang aktif |
| Awalnya sempurna, lalu berhenti di tengah struk | Payload melampaui buffer printer | Penulisan berpotongan dan jeda antar potongan |
| Semua terbaca tapi tidak ada yang rata di margin | Ukuran kertas salah, sehingga jumlah kolomnya salah | Argumen PaperSize pada generator |
| Simbol acak sejak karakter pertama | Dialek perintah salah atau tidak didukung | Capability profile dan urutan byte yang di-hardcode |
| Tidak ada apa-apa, tetapi aplikasi melaporkan sukses | Koneksi basi, atau printer sedang tidur | Logika connect, reconnect, dan percobaan ulang |

Ini laporan paling umum sekaligus paling mudah diperbaiki. Printer menyimpan tabel karakter satu byte dan mencetak dari tabel mana pun yang aktif; urutan UTF-8 tiba sebagai byte-byte terpisah, sehingga masing-masing menjadi glifnya sendiri.
// Symptom: "Rp 25.000" prints as "Rp 25.000" on one printer and as
// two stray glyphs on another. Cause: the bytes were UTF-8 and the
// printer was reading them through a single-byte code page.
// Wrong — hands raw UTF-8 to a printer that has never heard of it.
bytes.addAll(utf8.encode('Café Anggrek'));
// Right — let the generator encode to the active code page, and pick
// a capability profile that matches the hardware.
final profile = await CapabilityProfile.load(name: 'default');
final generator = Generator(PaperSize.mm58, profile);
bytes.addAll(generator.text('Cafe Anggrek')); // ASCII survives anywhere
// Safest of all for shop data you do not control: normalise first.
String asciiFold(String input) => input
.replaceAll(RegExp(r'[\u2018\u2019]'), "'")
.replaceAll(RegExp(r'[\u201C\u201D]'), '"')
.replaceAll('\u00A0', ' ');Dua pertahanan. Biarkan generator meng-encode teks lewat capability profile alih-alih Anda mendorong byte mentah sendiri, dan normalkan data toko yang masuk — katalog pemasok penuh apostrof melengkung dan spasi non-breaking yang selamat melewati basis data lalu mati di kepala cetak.
Hati-hati kalau hanya menguji dengan nama produk buatan sendiri. Data pengembangan adalah ASCII yang bersih; data toko sungguhan berisi tanda derajat, tanda pisah en yang ditempel dari spreadsheet, dan sesekali emoji di nama produk. Uji dengan salinan katalog asli pelanggan sebelum rilis.
Printer 58 mm punya buffer kecil dan, lewat Bluetooth serial, sering tanpa flow control yang berguna. Tulis struk panjang dalam satu panggilan dan ekornya bisa hilang begitu saja. Petunjuknya: kegagalan berkorelasi dengan panjang struk — uji tiga baris selalu berhasil, tiket dapur empat puluh baris gagal.
/// Symptom: the top of the receipt is perfect and the bottom is
/// missing, or a long receipt prints as fragments. Cause: the payload
/// outran a small serial buffer with no flow control in between.
const chunkSize = 256; // conservative; some firmware wants 128
const pauseBetweenChunks = Duration(milliseconds: 40);
Future<void> sendChunked(List<int> bytes) async {
for (var offset = 0; offset < bytes.length; offset += chunkSize) {
final end = (offset + chunkSize).clamp(0, bytes.length);
await PrintBluetoothThermal.writeBytes(bytes.sublist(offset, end));
await Future.delayed(pauseBetweenChunks);
}
}
// Tune chunkSize and the pause together, and measure with the longest
// receipt the shop actually prints — a 40-line kitchen ticket, not the
// three-line demo that always worked.Memotongnya dengan jeda pendek memperbaiki hal ini, dan angkanya spesifik per perangkat keras. Mulai dari nilai konservatif, lalu ukur dengan struk terpanjang yang benar-benar dicetak toko. Jeda yang terlalu pendek memunculkan lagi pemotongan; yang terlalu panjang membuat meja kasir sibuk terasa lelet, jadi setel kedua nilainya bersamaan alih-alih menggandakan salah satunya secara buta.

Sisa tabelnya cepat terselesaikan begitu Anda tahu apa yang harus diperiksa.
Poin terakhir layak ditegaskan karena paling banyak membuang waktu rekayasa. Kalau self test printer sendiri sudah pudar, hentikan debugging aplikasi Anda.
Ketika laporan masuk, bekerjalah naik dari perangkat keras, bukan turun dari kode. Setiap langkah menambah tepat satu lapisan, jadi kegagalan pertama langsung menyebut pelakunya.
/// The five-minute triage script. Run it before reading any app code.
/// Each step isolates one layer, and the first one that fails is the bug.
///
/// 1. Print the printer's own self test (hold FEED while powering on)
/// -> proves paper, head, battery, firmware
/// 2. Send ESC @ then "HELLO" then a cut, nothing else
/// -> proves pairing, SPP link and write path
/// 3. Send the same with double height and bold
/// -> proves the style commands the profile is emitting
/// 4. Send one full receipt from a committed byte fixture
/// -> proves the builder, in isolation from live data
/// 5. Send the live receipt that failed
/// -> whatever changed between 4 and 5 is your defectMenyimpan byte fixture yang di-commit untuk langkah keempat itulah yang membuat proses ini cepat. Ia menyingkirkan data langsung, kondisi jaringan, dan ERP dari gambaran, sehingga kalau fixture-nya tercetak dan struk langsungnya tidak, cacatnya terbukti ada pada cara struk dibangun, bukan pada cara ia dikirim.
Tambahkan layar developer tersembunyi yang membuang payload terakhir sebagai heksa dan bisa mengirimnya ulang. Bisa meminta toko menekan dua tombol lalu membacakan enam belas byte pertama menggantikan spekulasi sepanjang satu hari penuh.
Tiga kebiasaan mencegah bug-bug ini berulang di aplikasi yang saya rawat.
Pencetakan termal Bluetooth terlihat tidak andal terutama karena kegagalannya senyap. Beri diri Anda lingkar umpan balik — baca kertasnya, isolasi satu lapisan sekaligus, simpan byte fixture — dan lima penyebab yang sama akan menjelaskan hampir setiap struk kacau yang fotonya pernah dikirimkan kepada Anda.