← Tüm rehberler Mobil · Epson

Web Bluetooth ile Tarayıcıdan Direkt Termal Yazdırma Rehberi (Sürücüsüz Web POS)

Modern web tabanlı POS (Satış Noktası), kafe sipariş ve saha satış sistemlerinde en büyük operasyonel zorluk, tarayıcıdan fiş yazdırmaktır. Geleneksel yöntemlerde işletmeler; işletim sistemi sürücüleri kurmak, Windows Print Spooler yapılandırmak veya yerel arka plan servisleri (Electron, Tray Agent) çalıştırmak zorunda kalır.

Web Bluetooth API, bu karmaşık ara katmanları tamamen ortadan kaldırır. Google Chrome, Microsoft Edge ve Opera gibi Chromium tabanlı modern tarayıcılarda çalışan saf bir JavaScript koduyla; tablet veya bilgisayarınızdan doğrudan taşınabilir bir Bluetooth termal yazıcıya bağlanabilir ve milisaniyeler içinde ESC/POS fiş basabilirsiniz.

Bu kapsamlı mühendislik rehberinde; Web Bluetooth mimarisini, GATT servis ve karakteristik eşleşmesini, Bluetooth 20-byte MTU paketleme sınırını ve üretime hazır tam bir JavaScript sürücüsüz yazdırma kütüphanesini ele alıyoruz.


1. Web Bluetooth Mimarisi ve Güvenlik Gereksinimleri

Tarayıcıların donanım seviyesinde Bluetooth cihazlarına erişmesi yüksek güvenlik önlemleri gerektirir. W3C Web Bluetooth standardı şu temel kurallara bağlıdır:

  1. Yalnızca Güvenli Bağlantı (HTTPS): Web Bluetooth API yalnızca https:// protokolü üzerinden veya yerel geliştirme için localhost ortamında çalışır.
  2. Kullanıcı Etkileşimi Zorunluluğu (User Gesture): Bir web sayfası arka planda gizlice Bluetooth taraması yapamaz. navigator.bluetooth.requestDevice() çağrısı mutlaka bir buton tıklaması (click), dokunma (touchstart) gibi doğrudan bir kullanıcı hareketi tarafından tetiklenmelidir.
  3. Chromium Desteği: Android Chrome, Windows 10/11 Chrome/Edge, macOS Chrome ve ChromeOS üzerinde yerel olarak desteklenir. (iOS Safari için Bluefy veya WebBLE gibi özel tarayıcılar gerekir).

2. Bluetooth GATT, Servisler ve Karakteristikler

Bluetooth Low Energy (BLE) ve klasik Bluetooth SPP üzerinden haberleşen termal yazıcılar, GATT (Generic Attribute Profile) mimarisini kullanır. Bir yazıcıya veri gönderebilmek için yazıcının Veri Yazma Karakteristiğine (Write Characteristic) erişmeniz gerekir.

Piyasadaki termal yazıcıların (Epson, Xprinter, Zebra, Hoin, Goojprt) kullandığı standart Servis UUID’leri şunlardır:

Servis TipiServis UUIDKarakteristik UUID (Yazma)Üretici / Model
Standart Yazıcı Servisi000018f0-0000-1000-8000-00805f9b34fb00002af1-0000-1000-8000-00805f9b34fbStandart BLE Yazıcılar
Özel SPP Seri Port Servisi0000e781-0000-1000-8000-00805f9b34fb0000bef8-0000-1000-8000-00805f9b34fbÇoğu Taşınabilir 58mm/80mm
Xprinter / Generic BLE49535343-fe7d-4ae5-8fa9-9fafd205e45549535343-8841-43f4-a8d4-ecbe34729bb3Xprinter, Zjiang, Milestone

3. En Büyük Tuzak: 20-Byte MTU Limiti ve Akış Kontrolü

Web Bluetooth üzerinden bir termal yazıcıya 2 KB boyutunda bir fiş verisini tek seferde characteristic.writeValue() ile gönderirseniz tarayıcı derhal NetworkError: GATT operation failed hatası fırlatır veya yazıcı satırları atlayarak sadece fişin ilk 2-3 satırını basar.

Nedeni BLE MTU (Maximum Transmission Unit) sınırıdır. Standart BLE paket boyutu 23 bayttır; 3 bayt protokol başlığı düşüldüğünde net veri yükü 20 bayttır.

Çözüm: Paket Parçalama (Chunking) Algoritması

Yazdırılacak bayt dizisi (Uint8Array) 20’şer baytlık dilimlere bölünmeli ve her dilim yazıcıya sırayla gönderilmelidir. Taşma (buffer overflow) yaşanmaması için her paket arasına 10-20 milisaniye gecikme konulmalıdır:

async function sendChunkedData(characteristic, data, chunkSize = 20, delayMs = 15) {
  for (let i = 0; i < data.length; i += chunkSize) {
    const chunk = data.slice(i, i + chunkSize);
    // writeValueWithoutResponse daha hızlıdır; desteklenmiyorsa writeValueWithResponse kullanılır
    if (characteristic.properties.writeWithoutResponse) {
      await characteristic.writeValueWithoutResponse(chunk);
    } else {
      await characteristic.writeValueWithResponse(chunk);
    }
    
    // Yazıcı buffer'ının rahatlaması için kısa gecikme
    if (delayMs > 0) {
      await new Promise(resolve => setTimeout(resolve, delayMs));
    }
  }
}

4. Baştan Sona Çalışan Web Bluetooth POS Yazdırma Kodu

Aşağıdaki JavaScript kodu; cihaz eşleştirme, bağlantı kurma, ESC/POS fiş verisi üretme ve dilimleyerek yazdırma adımlarının tamamını içerir:

<!DOCTYPE html>
<html lang="tr">
<head>
  <meta charset="UTF-8">
  <title>Web Bluetooth Termal Yazdırma Demo</title>
</head>
<body>
  <h2>Printzen Web Bluetooth Yazdırma</h2>
  <button id="btnPrint" style="padding: 12px 24px; font-size: 16px; cursor: pointer;">
    Bluetooth Fiş Yazıcıya Bağlan ve Yazdır
  </button>

  <script>
    document.getElementById('btnPrint').addEventListener('click', async () => {
      try {
        console.log('Bluetooth cihaz taranıyor...');
        
        // 1. Kullanıcıdan cihaz seçmesini iste
        const device = await navigator.bluetooth.requestDevice({
          filters: [
            { services: ['000018f0-0000-1000-8000-00805f9b34fb'] },
            { services: ['0000e781-0000-1000-8000-00805f9b34fb'] },
            { services: ['49535343-fe7d-4ae5-8fa9-9fafd205e455'] }
          ],
          optionalServices: [
            '000018f0-0000-1000-8000-00805f9b34fb',
            '0000e781-0000-1000-8000-00805f9b34fb',
            '49535343-fe7d-4ae5-8fa9-9fafd205e455'
          ]
        });

        console.log('Cihaza bağlanılıyor:', device.name);
        const server = await device.gatt.connect();

        // 2. Aktif servisi ve yazma karakteristiğini bul
        let targetCharacteristic = null;
        const services = await server.getPrimaryServices();
        
        for (const service of services) {
          const characteristics = await service.getCharacteristics();
          for (const char of characteristics) {
            if (char.properties.write || char.properties.writeWithoutResponse) {
              targetCharacteristic = char;
              break;
            }
          }
          if (targetCharacteristic) break;
        }

        if (!targetCharacteristic) {
          throw new Error('Yazıcıda yazılabilir GATT karakteristiği bulunamadı.');
        }

        console.log('Karakteristik bulundu, fiş hazırlanıyor...');

        // 3. ESC/POS Fiş Komutlarını Hazırla
        const commands = [];
        
        // ESC @ -> Yazıcıyı sıfırla
        commands.push(0x1B, 0x40);
        
        // ESC a 1 -> Ortala
        commands.push(0x1B, 0x61, 0x01);
        
        // Metin verisi: Başlık
        const titleBytes = new TextEncoder().encode("PRINTZEN WEB POS\nSiparis Fisi\n--------------------------------\n");
        titleBytes.forEach(b => commands.push(b));
        
        // ESC a 0 -> Sola hizala
        commands.push(0x1B, 0x61, 0x00);
        
        const bodyBytes = new TextEncoder().encode(
          "1x Filtre Kahve             65.00 TL\n" +
          "1x Havuclu Kek              85.00 TL\n" +
          "--------------------------------\n" +
          "TOPLAM:                    150.00 TL\n\n" +
          "Web Bluetooth ile Yazdirildi.\n\n\n"
        );
        bodyBytes.forEach(b => commands.push(b));

        // GS V 66 0 -> Kağıt Kes
        commands.push(0x1D, 0x56, 66, 0);

        const payload = new Uint8Array(commands);

        // 4. 20-baytlık dilimler halinde gönder
        console.log('Baskı verisi gönderiliyor...');
        const CHUNK_SIZE = 20;
        for (let i = 0; i < payload.length; i += CHUNK_SIZE) {
          const slice = payload.slice(i, i + CHUNK_SIZE);
          if (targetCharacteristic.properties.writeWithoutResponse) {
            await targetCharacteristic.writeValueWithoutResponse(slice);
          } else {
            await targetCharacteristic.writeValueWithResponse(slice);
          }
          await new Promise(r => setTimeout(r, 15));
        }

        console.log('Fiş başarıyla yazdırıldı!');
        
      } catch (error) {
        console.error('Yazdırma hatası:', error);
        alert('Yazdırma başarısız: ' + error.message);
      }
    });
  </script>
</body>
</html>

5. Tarayıcı ve Platform Uyumluluk Tablosu

Platform / İşletim SistemiTarayıcıWeb Bluetooth DurumuNotlar
AndroidGoogle ChromeTam Destek (Kullanıma Hazır)Konum (Location) ve Bluetooth açık olmalı
Windows 10 / 11Chrome / EdgeTam DestekWindows Bluetooth Ayarlarından eşleştirme gerekebilir
macOSGoogle ChromeTam DestekSistem Tercihlerinde Chrome için Bluetooth izni verilmeli
ChromeOSStandart TarayıcıMükemmel Yerel DestekPOS kiosk terminalleri için ideal
iOS / iPadOSSafariDesteklenmiyorApple Safari Web Bluetooth’u engeller; Bluefy tarayıcısı kullanılabilir

6. Sıkça Sorulan Sorular (SSS)

Web Bluetooth ile yazdırmak için bilgisayara yazıcı sürücüsü kurmak gerekir mi?

Hayır, Web Bluetooth doğrudan işletim sisteminin Bluetooth radyo katmanıyla konuşur ve yazıcı sürücülerine (Driver) tamamen baypas eder. Tarayıcı, ham ESC/POS baytlarını doğrudan yazıcının GATT karakteristiğine ilettiği için herhangi bir driver veya Windows Print Spooler kurulumuna ihtiyaç yoktur.

Web Bluetooth fiş yazıcıyı bulamıyor veya listede göstermiyor, neden?

Bu durum genellikle requestDevice çağrısındaki filters parametresinin çok kısıtlayıcı olmasından veya yazıcının klasik Bluetooth (Bluetooth Classic 2.0/3.0) kullanıp BLE (Bluetooth Low Energy 4.0+) desteklememesinden kaynaklanır. Eski model bazı termal yazıcılar sadece Bluetooth SPP Classic destekler ve Web Bluetooth API tarafından taranamayabilir. Ayrıca cihazın başka bir telefona bağlı olmadığından emin olunmalıdır.

Baskı sırasında yazıcı neden donuyor veya satırları yarım kesiyor?

Bunun temel sebebi Bluetooth MTU sınırının (20 bayt) aşılmasıdır. Veriyi tek bir blok halinde göndermeye çalıştığınızda yazıcının dahili alıcı tamponu (buffer) taşar. Veriyi mutlaka 20’şer baytlık paketlere bölmeli ve paketler arasına 10-15 milisaniye gecikme eklemelisiniz.

iOS (iPhone ve iPad) cihazlarda tarayıcıdan Bluetooth fiş yazdırmak mümkün müdür?

Apple Safari Web Bluetooth API’sini desteklememektedir. Ancak App Store’dan indirebileceğiniz Bluefy veya WebBLE gibi özel geliştirici tarayıcıları Web Bluetooth standardını iOS üzerinde tam olarak destekler. Kurumsal projelerde saha ekiplerine bu tarayıcılar üzerinden Web POS kullandırılabilir veya Printzen Cloud Print servisi tercih edilebilir.