Kablosuz Yığını
TEKNİK REHBER KABLOSUZ YIĞINI BLE GATT 2026

BLE GATT
servis yazma.

GAP, GATT, ATT — BLE protokol temellerinden BlueZ D-Bus ile özel GATT servisine, Yocto entegrasyonuna kadar tam rehber.

00 BLE temel kavramlar — Advertising, GAP, GATT, ATT

Bluetooth Low Energy (BLE), Bluetooth 4.0 ile gelen düşük güç tüketimli versiyondur. IoT sensör düğümleri için tasarlanmıştır.

  BLE Protokol Yığını
  ──────────────────────────────────────────
  GATT        Generic Attribute Profile
              → Servis ve karakteristik tanımları
  GAP         Generic Access Profile
              → Advertising, connection, security
  ATT         Attribute Protocol
              → GATT'ın taşıma katmanı (client/server)
  L2CAP       Logical Link Control and Adaptation
              → Paket segmentasyonu, channel multiplexing
  HCI         Host Controller Interface
  LL          Link Layer → advertising, connections
  PHY         Physical Layer → 2.4 GHz RF
    

Advertising (reklam yayını)

BLE cihazları sürekli bağlantı tutmak yerine periyodik olarak advertisement paketleri yayınlar. Central (merkez) cihaz bu paketleri dinler (scanning) ve bağlanmak isterse Connection Request gönderir.

Advertiser (Peripheral)
Veri yayınlayan cihaz — sensör, beacon, akıllı saat. Genellikle pil ile çalışır.
Scanner (Central)
Tarama yapan cihaz — akıllı telefon, gateway, Raspberry Pi.
Advertisement interval
Paket yayın aralığı (20ms – 10.24s). Düşük aralık = hızlı keşif ama yüksek güç.
ADV_IND
Bağlantıya izin veren, tüm cihazlara yayın advertisement.
ADV_NONCONN_IND
Bağlantısız sadece veri yayını (beacon, iBeacon).
SCAN_RSP
Scanner'ın aktif tarama isteğine ek veri yanıtı.

GAP rolleri

BLE bağlantı kurulumu akışı
Peripheral (sensör)          Central (telefon/Pi)
      │                              │
      │── ADV_IND ──────────────────►│  advertising
      │                              │
      │◄─ CONNECT_REQ ──────────────│  central bağlanmak istiyor
      │                              │
      │══════ Connection ════════════│  bağlantı kuruldu
      │                              │
      │◄─ ATT Read Request ─────────│  central veri okuyor
      │── ATT Read Response ────────►│
      │                              │
      │── ATT Handle Value Notification ──►│  peripheral bildirim gönderiyor

01 GATT hiyerarşisi — Profile, Service, Characteristic, Descriptor

GATT, BLE veri modelinin omurgasıdır. Attribute tablosunu hiyerarşik olarak organize eder.

  Profile           (koleksiyon — örn: Health Thermometer Profile)
    └── Service       (işlevsel grup — örn: Temperature Service, 0x1809)
          ├── Characteristic  (veri birimi — örn: Temperature Measurement, 0x2A1C)
          │       ├── Value          (ham veri: bytes)
          │       └── Descriptor    (metadata — örn: CCCD, Unit, Description)
          └── Characteristic  (örn: Measurement Interval, 0x2A21)
    

ATT handle ve attribute tablosu

Her attribute (service, characteristic, descriptor) bir 16-bit handle'a sahiptir. ATT protokolü bu handle'lar üzerinden çalışır:

ATT attribute tablosu örneği
Handle  Type UUID              Value
──────  ──────────────────────────────────────────────────────────
0x0001  2800 (Primary Service)  1809 (Health Thermometer Service)
0x0002  2803 (Characteristic)   Properties: NOTIFY + READ
0x0003  2A1C (Temp Measurement) [value bytes]
0x0004  2902 (CCCD)             [00 00] → notify kapalı / [01 00] → açık
0x0005  2803 (Characteristic)   Properties: READ
0x0006  2A21 (Meas. Interval)   [0A 00] → 10 saniye

Temel servis UUID'leri (Bluetooth SIG)

0x1800
Generic Access — cihaz adı, görünüm.
0x1801
Generic Attribute — servis değişikliği bildirimi.
0x180A
Device Information — üretici, model, firmware.
0x180F
Battery Service — pil yüzdesi.
0x1809
Health Thermometer — sıcaklık ölçümü.
0x181A
Environmental Sensing — sıcaklık, nem, basınç.

02 BlueZ D-Bus GATT server API

BlueZ, GATT server oluşturmak için uygulama tarafından D-Bus nesneleri export edilmesini bekler. "D-Bus object model" yaklaşımı.

  Uygulama (Python)                        bluetoothd
  ──────────────────────────────────────────────────────
  D-Bus'a nesne kaydet:                    RegisterApplication() ile
    /app/service0                    →     GATT nesnelerini keşfeder
    /app/service0/char0                     ve BLE stack'e ekler
    /app/service0/char0/desc0
         ↓
  org.bluez.GattService1
  org.bluez.GattCharacteristic1
  org.bluez.GattDescriptor1
    

Gerekli D-Bus arayüzleri

org.bluez.GattService1
UUID (string), Primary (bool). İçerdiği karakteristikler alt path'lerde.
org.bluez.GattCharacteristic1
UUID, Flags (list), Service (path). ReadValue(), WriteValue(), StartNotify(), StopNotify() metodları.
org.bluez.GattDescriptor1
UUID, Characteristic (path). ReadValue(), WriteValue() metodları.
org.bluez.GattManager1
Adapter üzerindedir. RegisterApplication() ve UnregisterApplication() ile GATT app kaydedilir.
org.bluez.LEAdvertisement1
Type, ServiceUUIDs, ManufacturerData, LocalName. Advertising verisi.
org.bluez.LEAdvertisingManager1
RegisterAdvertisement() ile advertisement kaydedilir.

03 Python ile custom GATT service — sıcaklık sensörü

BlueZ D-Bus GATT API kullanarak sıcaklık değerini BLE üzerinden yayınlayan tam çalışır örnek.

python — ble_temperature_server.py
#!/usr/bin/env python3
"""BLE GATT Sıcaklık Sensörü — BlueZ D-Bus API ile."""

import dbus
import dbus.exceptions
import dbus.mainloop.glib
import dbus.service
from gi.repository import GLib
import struct
import random

BLUEZ_SERVICE = 'org.bluez'
GATT_MANAGER_IFACE = 'org.bluez.GattManager1'
GATT_SERVICE_IFACE = 'org.bluez.GattService1'
GATT_CHAR_IFACE = 'org.bluez.GattCharacteristic1'
GATT_DESC_IFACE = 'org.bluez.GattDescriptor1'
LE_ADV_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'
LE_ADV_IFACE = 'org.bluez.LEAdvertisement1'
DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'

# Sıcaklık servisi için UUID'ler (SIG standartları)
TEMP_SERVICE_UUID = '0000180d-0000-1000-8000-00805f9b34fb'   # Heart Rate → örnek
TEMP_CHAR_UUID   = '00002a37-0000-1000-8000-00805f9b34fb'
# Gerçek Environmental Sensing için:
ENV_SERVICE_UUID = '0000181a-0000-1000-8000-00805f9b34fb'
TEMP_MEAS_UUID   = '00002a6e-0000-1000-8000-00805f9b34fb'   # Temperature 2A6E
CCCD_UUID        = '00002902-0000-1000-8000-00805f9b34fb'


class InvalidArgsException(dbus.exceptions.DBusException):
    _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'


class Application(dbus.service.Object):
    """GATT uygulaması — tüm servislerin kök nesnesi."""

    def __init__(self, bus):
        self.path = '/'
        self.services = []
        dbus.service.Object.__init__(self, bus, self.path)
        self.add_service(TemperatureService(bus, 0))

    def get_path(self):
        return dbus.ObjectPath(self.path)

    def add_service(self, service):
        self.services.append(service)

    @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
    def GetManagedObjects(self):
        response = {}
        for service in self.services:
            response[service.get_path()] = service.get_properties()
            chrcs = service.get_characteristics()
            for chrc in chrcs:
                response[chrc.get_path()] = chrc.get_properties()
                descs = chrc.get_descriptors()
                for desc in descs:
                    response[desc.get_path()] = desc.get_properties()
        return response


class Service(dbus.service.Object):
    PATH_BASE = '/org/bluez/example/service'

    def __init__(self, bus, index, uuid, primary):
        self.path = self.PATH_BASE + str(index)
        self.bus = bus
        self.uuid = uuid
        self.primary = primary
        self.characteristics = []
        dbus.service.Object.__init__(self, bus, self.path)

    def get_properties(self):
        return {
            GATT_SERVICE_IFACE: {
                'UUID': self.uuid,
                'Primary': self.primary,
                'Characteristics': dbus.Array(
                    [c.get_path() for c in self.characteristics],
                    signature='o')
            }
        }

    def get_path(self):
        return dbus.ObjectPath(self.path)

    def add_characteristic(self, characteristic):
        self.characteristics.append(characteristic)

    def get_characteristics(self):
        return self.characteristics

    @dbus.service.method(DBUS_PROP_IFACE, in_signature='s',
                         out_signature='a{sv}')
    def GetAll(self, interface):
        if interface != GATT_SERVICE_IFACE:
            raise InvalidArgsException()
        return self.get_properties()[GATT_SERVICE_IFACE]


class Characteristic(dbus.service.Object):

    def __init__(self, bus, index, uuid, flags, service):
        self.path = service.path + '/char' + str(index)
        self.bus = bus
        self.uuid = uuid
        self.service = service
        self.flags = flags
        self.descriptors = []
        dbus.service.Object.__init__(self, bus, self.path)

    def get_properties(self):
        return {
            GATT_CHAR_IFACE: {
                'Service': self.service.get_path(),
                'UUID': self.uuid,
                'Flags': self.flags,
                'Descriptors': dbus.Array(
                    [d.get_path() for d in self.descriptors],
                    signature='o')
            }
        }

    def get_path(self):
        return dbus.ObjectPath(self.path)

    def add_descriptor(self, descriptor):
        self.descriptors.append(descriptor)

    def get_descriptors(self):
        return self.descriptors

    @dbus.service.method(DBUS_PROP_IFACE, in_signature='s',
                         out_signature='a{sv}')
    def GetAll(self, interface):
        if interface != GATT_CHAR_IFACE:
            raise InvalidArgsException()
        return self.get_properties()[GATT_CHAR_IFACE]

    @dbus.service.signal(DBUS_PROP_IFACE, signature='sa{sv}as')
    def PropertiesChanged(self, interface, changed, invalidated):
        pass


class TemperatureService(Service):
    """Environmental Sensing Service — sıcaklık ölçümü."""

    def __init__(self, bus, index):
        Service.__init__(self, bus, index, ENV_SERVICE_UUID, True)
        self.add_characteristic(TemperatureCharacteristic(bus, 0, self))


class TemperatureCharacteristic(Characteristic):
    """Sıcaklık karakteristiği — READ + NOTIFY."""

    def __init__(self, bus, index, service):
        Characteristic.__init__(
            self, bus, index, TEMP_MEAS_UUID,
            ['read', 'notify'], service)
        self.notifying = False
        self.add_descriptor(CCCDescriptor(bus, 0, self))

    def get_temperature(self):
        """Gerçek sistemde DS18B20 veya BME280'den oku."""
        temp_c = 23.5 + random.uniform(-0.5, 0.5)  # Simüle
        # GATT Temperature 2A6E: int16, 0.01 °C çözünürlük
        raw = int(temp_c * 100)
        return struct.pack('<h', raw)  # little-endian signed 16-bit

    @dbus.service.method(GATT_CHAR_IFACE, in_signature='a{sv}',
                         out_signature='ay')
    def ReadValue(self, options):
        data = self.get_temperature()
        print(f"Sıcaklık okuma: {struct.unpack('<h', data)[0] / 100:.2f} °C")
        return dbus.Array(data, signature='y')

    @dbus.service.method(GATT_CHAR_IFACE, in_signature='', out_signature='')
    def StartNotify(self):
        if self.notifying:
            return
        print("Notify başladı.")
        self.notifying = True
        GLib.timeout_add(2000, self._send_notification)  # 2 saniyede bir

    @dbus.service.method(GATT_CHAR_IFACE, in_signature='', out_signature='')
    def StopNotify(self):
        print("Notify durdu.")
        self.notifying = False

    def _send_notification(self):
        if not self.notifying:
            return False
        data = self.get_temperature()
        self.PropertiesChanged(
            GATT_CHAR_IFACE,
            {'Value': dbus.Array(data, signature='y')},
            [])
        return True  # GLib timeout devam etsin


class CCCDescriptor(dbus.service.Object):
    """Client Characteristic Configuration Descriptor (CCCD) — 0x2902."""

    def __init__(self, bus, index, characteristic):
        self.path = characteristic.path + '/desc' + str(index)
        self.bus = bus
        self.characteristic = characteristic
        dbus.service.Object.__init__(self, bus, self.path)

    def get_properties(self):
        return {
            GATT_DESC_IFACE: {
                'Characteristic': self.characteristic.get_path(),
                'UUID': CCCD_UUID,
                'Flags': ['read', 'write'],
            }
        }

    def get_path(self):
        return dbus.ObjectPath(self.path)

    @dbus.service.method(GATT_DESC_IFACE, in_signature='a{sv}',
                         out_signature='ay')
    def ReadValue(self, options):
        notify = self.characteristic.notifying
        return dbus.Array([0x01 if notify else 0x00, 0x00], signature='y')

    @dbus.service.method(GATT_DESC_IFACE, in_signature='aya{sv}',
                         out_signature='')
    def WriteValue(self, value, options):
        if value[0]:
            self.characteristic.StartNotify()
        else:
            self.characteristic.StopNotify()

    @dbus.service.method(DBUS_PROP_IFACE, in_signature='s',
                         out_signature='a{sv}')
    def GetAll(self, interface):
        return self.get_properties()[GATT_DESC_IFACE]


def main():
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
    bus = dbus.SystemBus()

    # Adapter al
    adapter_obj = bus.get_object(BLUEZ_SERVICE, '/org/bluez/hci0')
    gatt_manager = dbus.Interface(adapter_obj, GATT_MANAGER_IFACE)

    app = Application(bus)

    mainloop = GLib.MainLoop()

    def register_ok():
        print("GATT uygulaması kaydedildi. BLE yayını başladı.")

    def register_err(error):
        print(f"GATT kaydı hatası: {error}")
        mainloop.quit()

    gatt_manager.RegisterApplication(
        app.get_path(), {},
        reply_handler=register_ok,
        error_handler=register_err)

    print("Bağlantı bekleniyor... (Ctrl+C ile durdur)")
    try:
        mainloop.run()
    except KeyboardInterrupt:
        gatt_manager.UnregisterApplication(app.get_path())
        print("Durduruldu.")


if __name__ == '__main__':
    main()

04 UUID tanımlama — SIG vs özel 128-bit

BLE UUID'leri iki türdür: Bluetooth SIG tarafından atanmış 16-bit UUID'ler ve üreticinin tanımladığı 128-bit özel UUID'ler.

SIG UUID'leri (16-bit)

SIG UUID örnekleri
# 16-bit kısa form
0x180A   Device Information Service
0x180F   Battery Service
0x1809   Health Thermometer Service
0x181A   Environmental Sensing Service

0x2A29   Manufacturer Name String
0x2A24   Model Number String
0x2A19   Battery Level
0x2A6E   Temperature (0.01 °C çözünürlük, int16)
0x2A6F   Humidity (0.01 % çözünürlük, uint16)
0x2A75   Pollen Concentration

# 128-bit tam form (Base UUID ile)
# 0x0000XXXX-0000-1000-8000-00805F9B34FB
# Örnek: 0x180F →
0000180F-0000-1000-8000-00805F9B34FB

Özel 128-bit UUID tanımlama

bash — UUID üretimi
# Rastgele UUID üret
uuidgen
# 12345678-1234-5678-1234-567812345678

# Python ile
python3 -c "import uuid; print(uuid.uuid4())"
# a1b2c3d4-e5f6-7890-abcd-ef1234567890
python — özel UUID'lerle servis
# Özel bir IoT cihazı için UUID hiyerarşisi tanımla

# Ana servis UUID (vendor-specific)
CUSTOM_SERVICE_UUID  = 'a1b2c3d4-0001-0000-0000-000000000001'

# Karakteristikler
SENSOR_READ_UUID     = 'a1b2c3d4-0001-0000-0000-000000000002'  # Oku
CONFIG_WRITE_UUID    = 'a1b2c3d4-0001-0000-0000-000000000003'  # Yaz
STATUS_NOTIFY_UUID   = 'a1b2c3d4-0001-0000-0000-000000000004'  # Bildirim

# UUID naming convention önerisi:
#   [vendor_prefix]-[service_id]-[version]-[reserved]-[char_index]
# Bu şekilde çakışma olmadan genişletilebilir.

UUID'yi Wireshark'ta filtrele

wireshark filter
# Bluetooth capture için
# sudo btmon -w /tmp/bt.log
# Wireshark'ta aç: btsnoop formatı

# Filtreler:
btatt.uuid16 == 0x2a6e           # Sıcaklık karakteristiği
btatt.opcode == 0x12             # Handle Value Notification
btle.advertising_header.pdu_type == 0  # ADV_IND paketleri

05 Characteristic properties — READ, WRITE, NOTIFY

Her karakteristiğin hangi işlemlere izin verdiğini belirleyen özellikler (flags/properties).

read
Client karakteristik değerini okuyabilir (ATT Read Request).
write
Client değer yazabilir (ATT Write Request, yanıt beklenir).
write-without-response
Client değer yazabilir, yanıt gelmez (ATT Write Command, hızlı ama güvenilmez).
notify
Server, CCCD etkinleştirilince client'a değer değişikliğini bildirir. Onay gerekmez.
indicate
notify gibi ama ATT onayı (ACK) gerekir. Kritik veriler için.
broadcast
Değeri advertisement paketine ekle (bağlantısız yayın için).
extended-properties
Ek özellikler için ExtendedProperties descriptor gerektirir.
encrypt-read
Okuma için şifrelenmiş bağlantı gerekir (eşleştirme zorunlu).
encrypt-write
Yazma için şifrelenmiş bağlantı gerekir.
secure-read / secure-write
Kimlik doğrulamalı şifreleme (LESC / Secure Connections).

Örnek flag kombinasyonları

python — farklı flag kombinasyonları
# Salt okunur sensör verisi
flags = ['read']

# Yazılabilir konfigürasyon (ör: örnekleme aralığı)
flags = ['read', 'write']

# Hızlı akış verisi (titreşim, mikrofon)
flags = ['read', 'notify']

# Kritik komut (aktüatör kontrolü)
flags = ['write', 'indicate']

# Güvenli yönetim kanalı
flags = ['encrypt-read', 'encrypt-write']

# Üretim kalibrasyonu (fabrika erişimi)
flags = ['secure-read', 'secure-write']

06 Notify/Indicate mekanizması ve MTU

Notify, sensör verilerini sürekli polling'e gerek kalmadan push etmenin temel yoludur.

Notify akışı

  Client                          Server (sensör)
    │                                 │
    │── ATT Write (CCCD = 01 00) ────►│  notify etkinleştir
    │                                 │
    │◄── ATT Handle Value Notif. ─────│  sunucu veri gönderir (her 2s)
    │◄── ATT Handle Value Notif. ─────│
    │◄── ATT Handle Value Notif. ─────│
    │                                 │
    │── ATT Write (CCCD = 00 00) ────►│  notify durdur
    

Indicate vs Notify

python — notify ve indicate farkı
# NOTIFY: tek yönlü, onaysız (hızlı, düşük overhead)
# ATT Opcode: 0x1B (Handle Value Notification)
self.PropertiesChanged(
    GATT_CHAR_IFACE,
    {'Value': dbus.Array(data, signature='y')},
    [])

# INDICATE: onaylı (güvenilir, yüksek overhead)
# ATT Opcode: 0x1D (Handle Value Indication) → client 0x1E (Confirmation) gönderir
# BlueZ D-Bus'ta indicate için flag 'indicate' kullanılır, aynı PropertiesChanged ile tetiklenir
# Konfirmasyon alınana kadar yeni indication gönderilemez

MTU (Maximum Transmission Unit)

python — MTU negotiation
# BLE varsayılan MTU: 23 bayt (3 bayt ATT header = 20 bayt payload)
# Modern cihazlar 256-517 bayt MTU destekler (BLE 4.2+ Data Length Extension)

# ReadValue() options içinde negotiated MTU gelir
@dbus.service.method(GATT_CHAR_IFACE, in_signature='a{sv}', out_signature='ay')
def ReadValue(self, options):
    mtu = options.get('mtu', 23)
    print(f"MTU: {mtu}")
    # Büyük veri için MTU'ya göre parçala (BLE stack otomatik yapar)
    data = self.get_large_data()
    return dbus.Array(data[:mtu-3], signature='y')

# WriteValue() ile alınan offset (Long Write için)
@dbus.service.method(GATT_CHAR_IFACE, in_signature='aya{sv}', out_signature='')
def WriteValue(self, value, options):
    offset = options.get('offset', 0)
    data = bytes(value)
    print(f"Yazılan: offset={offset}, {len(data)} bayt")

Notify veri hızı optimizasyonu

python — hızlı veri akışı
# Connection interval, notify throughput'u sınırlar
# Default: 7.5ms - 4000ms (tipik: 50ms)
# Yüksek throughput için: iw/hciconfig ile bağlantı param ayarla

# BlueZ ile connection parameters (sadece central role'da)
from pydbus import SystemBus

bus = SystemBus()
device = bus.get('org.bluez', '/org/bluez/hci0/dev_AA_BB_CC_DD_EE_01')
# Peripheral olarak biz notify ediyoruz, central bağlantı parametrelerini kontrol eder
# GLib timeout aralığını bağlantı interval'a yakın seç

# 10ms interval → ~100 notify/saniye teorik maksimum
GLib.timeout_add(10, send_sensor_data)

07 C ile GATT client — libbluetooth

Düşük seviye C kodu ile BLE cihaza bağlanma ve GATT karakteristiği okuma. libbluetooth (bluez-dev paketi) gerektirir.

NOT

libbluetooth, BlueZ'in alt seviye C kütüphanesidir. Doğrudan HCI soketi açar. Yeni projeler için D-Bus API önerilir — ancak libbluetooth minimal ortamlarda (daemon olmadan) kullanışlıdır.

bash — libbluetooth kurulum
apt-get install libbluetooth-dev
gcc gatt_client.c -o gatt_client -lbluetooth
c — gatt_client.c (L2CAP / ATT ile)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/l2cap.h>

/* ATT opcodes */
#define ATT_OP_READ_REQ  0x0A
#define ATT_OP_READ_RSP  0x0B
#define ATT_OP_WRITE_CMD 0x52

/* Sıcaklık karakteristiği handle (önceden bilinmeli) */
#define TEMP_CHAR_HANDLE 0x0003

int att_connect(const char *addr) {
    int sock;
    struct sockaddr_l2 sa;

    sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
    if (sock < 0) {
        perror("socket");
        return -1;
    }

    memset(&sa, 0, sizeof(sa));
    sa.l2_family = AF_BLUETOOTH;
    sa.l2_psm = 0;        /* ATT için PSM = 0 */
    sa.l2_cid = htobs(4); /* ATT CID = 4 */
    sa.l2_bdaddr_type = BDADDR_LE_PUBLIC;
    str2ba(addr, &sa.l2_bdaddr);

    if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
        perror("connect");
        close(sock);
        return -1;
    }
    return sock;
}

int att_read(int sock, uint16_t handle, uint8_t *out, int maxlen) {
    uint8_t req[3];
    uint8_t rsp[256];
    ssize_t len;

    /* ATT Read Request */
    req[0] = ATT_OP_READ_REQ;
    req[1] = handle & 0xFF;
    req[2] = (handle >> 8) & 0xFF;

    if (write(sock, req, sizeof(req)) < 0) return -1;

    len = read(sock, rsp, sizeof(rsp));
    if (len < 1) return -1;

    if (rsp[0] != ATT_OP_READ_RSP) {
        fprintf(stderr, "Beklenmedik opcode: 0x%02x\n", rsp[0]);
        return -1;
    }

    int data_len = len - 1;
    if (data_len > maxlen) data_len = maxlen;
    memcpy(out, rsp + 1, data_len);
    return data_len;
}

int main(void) {
    const char *addr = "AA:BB:CC:DD:EE:01";
    int sock;
    uint8_t data[32];
    int len;

    printf("BLE cihazına bağlanılıyor: %s\n", addr);
    sock = att_connect(addr);
    if (sock < 0) {
        fprintf(stderr, "Bağlantı başarısız\n");
        return 1;
    }
    printf("Bağlandı.\n");

    /* Sıcaklık oku */
    len = att_read(sock, TEMP_CHAR_HANDLE, data, sizeof(data));
    if (len == 2) {
        int16_t raw;
        memcpy(&raw, data, 2);
        printf("Sıcaklık: %.2f °C\n", raw / 100.0);
    } else {
        printf("Okunan veri: %d bayt\n", len);
    }

    close(sock);
    return 0;
}

08 Embedded — BlueZ Yocto entegrasyonu ve minimal BLE advertiser

Yocto ile özel imaj oluşturma ve D-Bus/Python'a gerek duymayan minimal BLE advertiser scripti.

Yocto'ya BlueZ ekleme

Yocto — local.conf / image recipe
# local.conf veya image recipe'ye ekle
IMAGE_INSTALL:append = " \
    bluez5 \
    bluez5-testtools \
    python3-pydbus \
    python3-pygobject \
    kernel-module-bluetooth \
    kernel-module-hci-uart \
    kernel-module-btbcm \
    kernel-module-rfkill \
"

# BlueZ kernel fragmenti
# meta-custom/recipes-kernel/linux/linux-%.bbappend:
SRC_URI += "file://bluetooth.cfg"

# bluetooth.cfg içeriği:
# CONFIG_BT=y
# CONFIG_BT_LE=y
# CONFIG_BT_RFCOMM=y
# CONFIG_BT_HCIUART=y
# CONFIG_BT_HCIUART_BCM=y  (Raspberry Pi için)

Minimal BLE Advertiser scripti (btmgmt ile)

bash — btmgmt ile BLE advertising
#!/bin/sh
# minimal_ble_advertiser.sh
# Python/D-Bus gerektirmez — btmgmt komut satırı aracı

# Adaptörü aç
btmgmt power on

# LE advertising etkinleştir
btmgmt le on

# Connectable advertising
btmgmt connectable on

# Advertising veri ayarla (Local Name: "IoT_Sensor")
btmgmt add-adv \
    --adv-data 02010602094954535f53656e736f72 \
    1

# 02 01 06     → Flags: LE General Discoverable, BR/EDR Not Supported
# 02 09 ...   → Complete Local Name: bytes of "IoT_Sensor"

echo "BLE advertising başladı."
echo "hcidump -i hci0 -X ile izleyebilirsin"

hcitool / hciconfig ile çok düşük seviye advertiser

bash — HCI komutları ile doğrudan advertising
# HCI komutları ile BLE advertising (bluetoothd gerekmez!)

# LE set advertising parameters
hcitool -i hci0 cmd 0x08 0x0006 \
    A0 00 A0 00 00 00 00 00 00 00 00 00 00 07 00

# LE set advertising data (max 31 bayt)
# Payload: 0x02 0x01 0x06 (Flags) + 0x0A 0x09 "IoTSensor"
hcitool -i hci0 cmd 0x08 0x0008 \
    0F 02 01 06 0A 09 49 6F 54 53 65 6E 73 6F 72 \
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

# LE set advertise enable
hcitool -i hci0 cmd 0x08 0x000A 01

echo "BLE advertiser aktif (bluetoothd olmadan)"

Buildroot konfigürasyonu

Buildroot .config
# BlueZ 5 userspace
BR2_PACKAGE_BLUEZ5_UTILS=y
BR2_PACKAGE_BLUEZ5_UTILS_CLIENT=y     # bluetoothctl
BR2_PACKAGE_BLUEZ5_UTILS_MONITOR=y    # btmon
BR2_PACKAGE_BLUEZ5_DAEMON=y           # bluetoothd

# Python GATT için
BR2_PACKAGE_PYTHON3=y
BR2_PACKAGE_PYTHON_DBUS=y
BR2_PACKAGE_PYTHON_PYGOBJECT=y

# D-Bus gerekli
BR2_PACKAGE_DBUS=y

# Kernel: BT_HCIUART sürücüsü (UART Bluetooth için)
# Linux kernel .config'e ekle:
# CONFIG_BT_HCIUART=y
# CONFIG_BT_HCIUART_BCM=y  (Broadcom/RPi)
# CONFIG_BT_HCIUART_LL=y   (Texas Instruments)
GELİŞTİRME TAVSİYESİ

GATT servisi geliştirirken ilk aşamada Raspberry Pi 4 üzerinde test et. Android'de "nRF Connect" uygulaması, iOS'ta "LightBlue" uygulaması ile karakteristikleri anlık izleyebilirsin. Stable olduktan sonra hedef gömülü platforma geç. btmon çıktısı ve Wireshark btsnoop formatı invaluable debug araçlarıdır.