Tüm eğitimler
TEKNİK REHBERGÖMÜLÜ LİNUXIOT PROTOKOLÜ2026

CoAP / LwM2M
IoT Cihaz Yönetim Protokolleri

Kısıtlı cihazlar için CoAP protokolü ve OMA LwM2M cihaz yönetim standardının gömülü Linux uygulaması, Wakaama ve Eclipse Leshan entegrasyonu.

00 CoAP Neden? HTTP'nin Sınırları

Klasik HTTP/REST mimarisi kısıtlı gömülü cihazlar için ciddi yük oluşturur. CoAP (Constrained Application Protocol), bu boşluğu doldurmak için RFC 7252 ile tanımlanmıştır.

Endüstriyel IoT'de milyonlarca sensör, birkaç KB RAM ile çalışan mikrodenetleyici veya MPU tabanlı kartlar üzerinde koşar. Bu cihazlarda TCP bağlantısı kurmak, TLS el sıkışması gerçekleştirmek ve HTTP başlıklarını işlemek hem bellek hem enerji açısından maliyetlidir. Standart bir HTTP GET isteği başlıklar dahil kolayca 500-1000 bayt yer kaplarken CoAP aynı isteği 4 baytlık sabit başlık ile gönderebilir.

OSI Katmanı    HTTP Stack         CoAP Stack
─────────────────────────────────────────────
Uygulama (7)   HTTP/1.1           CoAP (RFC 7252)
Sunum (6)      TLS/SSL            DTLS (RFC 6347)
Taşıma (4)     TCP                UDP
Ağ (3)         IPv4/IPv6          IPv4/IPv6 / 6LoWPAN
Veri Bağı (2)  Ethernet / Wi-Fi   IEEE 802.15.4 / BLE
  

CoAP'ın temel avantajları şunlardır:

UDP tabanlıBağlantı kurulum yükü yok; tek paket ile istek ve yanıt mümkün
4 bayt başlıkHTTP'ye kıyasla dramatik bant genişliği tasarrufu sağlar
Eşzamansız mesajlaşmaCON/NON mesaj tipleri ile güvenilirlik seviyeleri ayarlanabilir
Gözlem desteğiRFC 7641 ile kaynaklarda long-poll yerine sunucu itme bildirimleri
HTTP eşlemeCoAP ile HTTP arasında proxy ile mevcut web altyapısına doğal entegrasyon
6LoWPAN uyumluIEEE 802.15.4 ağlarında küçük paket boyutu ile sorunsuz çalışır

Enerji tüketimi açısından bakıldığında, bir CoAP isteği için gereken radyo açık kalma süresi HTTP'ye göre 10 kat daha kısa olabilir. Pil ile çalışan bir sensör düğümü, HTTP ile günler içinde bitirdiği pili CoAP ile aylarca koruyabilir. Bu nedenle akıllı enerji sayaçlarından tarımsal sensörlere, endüstriyel monitörlerden sağlık giyilebilirlerine kadar tüm kısıtlı IoT cihazları için CoAP fiili standart haline gelmiştir.

ÖzellikHTTP/1.1CoAP
Taşıma katmanıTCPUDP
Başlık boyutu200-500 bayt4 bayt sabit
GüvenlikTLSDTLS
MulticastDesteklenmezNON mesajlarıyla desteklenir
Gözlemleme (push)SSE/WebSocket gerekirYerleşik Observe seçeneği
İkili kodlamaMetinİkili; CBOR ile birleşir

01 CoAP Protokol Temelleri

CoAP, HTTP metotlarını UDP üzerine taşır ve mesaj güvenilirliği için dört farklı mesaj tipi tanımlar. Protokolü anlamak sağlam bir uygulama geliştirmenin temelidir.

Mesaj Tipleri

CON (Confirmable)Onay gerektiren güvenilir mesaj; ACK alınana dek yeniden gönderilir (exponential backoff)
NON (Non-confirmable)Onay gerektirmeyen best-effort mesaj; periyodik sensör verisi için uygundur
ACK (Acknowledgement)CON mesajının alındığını doğrular; piggybacked yanıt taşıyabilir
RST (Reset)Anlaşılamayan bir mesaja verilen hata yanıtı; bilinmeyen Message ID için kullanılır

CoAP Metotları ve Yanıt Kodları

CoAP, HTTP'nin GET/POST/PUT/DELETE metotlarını benimser ancak yanıt kodları farklı bir şema kullanır (sınıf.ayrıntı biçiminde):

Metot / KodAçıklama
GETKaynağı okur; gözlem için Observe seçeneği eklenir
POSTKaynak oluşturur veya mevcut kaynağa işlem uygular
PUTKaynağı tamamen günceller
DELETEKaynağı siler
2.05 ContentGET isteğine başarılı yanıt (HTTP 200 eşdeğeri)
2.01 CreatedPOST ile kaynak oluşturuldu (HTTP 201)
4.04 Not FoundKaynak bulunamadı (HTTP 404)
5.00 Internal Server ErrorSunucu taraflı hata (HTTP 500)

CoAP Seçenekleri (Options)

CoAP başlığına eklenen seçenekler, HTTP başlıklarının sıkıştırılmış eşdeğeridir. Her seçenek delta-kodlama ile sıkıştırılır:

Uri-Host / Uri-PathHedef kaynak yolu; coap://host/sensor/temp gibi URI oluşturur
Content-Format0 = text/plain, 50 = application/json, 60 = application/cbor
Observe (RFC 7641)0 = kayda ol, 1 = kaydı iptal et; sunucu değişiklikte bildirim gönderir
Block1 / Block2Büyük payload'ları parçalara böler (RFC 7959); OTA transferi için kritik
ETagİçerik versiyonu; önbellekleme ve gereksiz yanıtları önler
Max-AgeYanıtın önbellekte geçerli kalacağı süre (saniye cinsinden)

CoAP Paket Yapısı

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver| T |  TKL  |      Code     |          Message ID           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Token (0-8 bayt)                                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Secenekler (degisken) + 0xFF isareti + Payload              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Ver=01 (CoAP v1)  T=mesaj tipi  TKL=token uzunlugu (0-8 bayt)
Code=istek/yanit kodu  MsgID=16 bit benzersiz kimlik
  

CON/ACK Mesaj Akışı

Istemci                          Sunucu
   |                                |
   |-- CON GET /sensor/temp ------>|  (MsgID=0x4A2B)
   |                                |
   |<-- ACK 2.05 Content ----------|  (MsgID=0x4A2B, yanit)
   |    Payload: "23.5"             |
   |                                |
   |-- NON GET /sensor/hum ------->|  (best-effort, ACK beklenmez)
   |<-- NON 2.05 Content ----------|
  

02 libcoap ile CoAP Sunucu

libcoap, C dilinde tam özellikli bir CoAP yığını sunar. Gömülü Linux üzerinde CoAP kaynak sunucusu kurmak için en yaygın ve olgun seçenektir.

Kurulum

# Ubuntu / Debian tabanlı sistemler
sudo apt install libcoap3-dev coap-utils

# Yocto icin meta-openembedded/meta-networking katmani gerekir
# local.conf icine ekleyin:
IMAGE_INSTALL:append = " libcoap coap-utils"

# Kaynak koddan derleme
git clone https://github.com/obgm/libcoap.git
cd libcoap
./autogen.sh
./configure --enable-examples --enable-dtls --with-tinydtls
make -j4
sudo make install

Temel CoAP Sunucu — C Kodu

#include <coap3/coap.h>
#include <stdio.h>
#include <string.h>

/* Sicaklik kaynagi icin GET handler */
static void
hnd_get_temperature(coap_resource_t *resource,
                    coap_session_t  *session,
                    const coap_pdu_t *request,
                    const coap_string_t *query,
                    coap_pdu_t *response)
{
    char buf[32];
    float temp = 23.5f;  /* Gercek uygulamada sensordan okunur */

    snprintf(buf, sizeof(buf), "%.1f", temp);

    coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
    coap_add_option(response,
                    COAP_OPTION_CONTENT_FORMAT,
                    coap_encode_var_safe(NULL, 0,
                        COAP_MEDIATYPE_TEXT_PLAIN),
                    NULL);
    coap_add_data(response, strlen(buf), (uint8_t *)buf);
}

/* PUT handler: esik degerini guncelle */
static void
hnd_put_threshold(coap_resource_t *resource,
                  coap_session_t  *session,
                  const coap_pdu_t *request,
                  const coap_string_t *query,
                  coap_pdu_t *response)
{
    size_t size;
    const uint8_t *data;

    if (coap_get_data(request, &size, &data)) {
        float threshold = atof((const char *)data);
        printf("Yeni esik degeri: %.1f\n", threshold);
        coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED);
    } else {
        coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST);
    }
}

int main(void)
{
    coap_context_t  *ctx   = NULL;
    coap_address_t   addr;
    coap_endpoint_t *ep    = NULL;
    coap_resource_t *r_temp, *r_thresh;

    coap_startup();

    /* Dinleme adresi: tum arayuzler, 5683 portu */
    coap_address_init(&addr);
    addr.addr.sin.sin_family      = AF_INET;
    addr.addr.sin.sin_port        = htons(5683);
    addr.addr.sin.sin_addr.s_addr = INADDR_ANY;

    ctx = coap_new_context(NULL);
    if (!ctx) {
        fprintf(stderr, "Context olusturulamadi\n");
        return 1;
    }

    ep = coap_new_endpoint(ctx, &addr, COAP_PROTO_UDP);
    if (!ep) {
        fprintf(stderr, "Endpoint olusturulamadi\n");
        return 1;
    }

    /* /sensor/temperature kaynagi */
    r_temp = coap_resource_init(
                 coap_make_str_const("sensor/temperature"), 0);
    coap_register_handler(r_temp, COAP_REQUEST_GET,
                          hnd_get_temperature);
    coap_resource_set_observable(r_temp, 1); /* Observe destegi */
    coap_add_resource(ctx, r_temp);

    /* /sensor/threshold kaynagi */
    r_thresh = coap_resource_init(
                   coap_make_str_const("sensor/threshold"), 0);
    coap_register_handler(r_thresh, COAP_REQUEST_PUT,
                          hnd_put_threshold);
    coap_add_resource(ctx, r_thresh);

    printf("CoAP sunucu 5683 portunda dinleniyor...\n");

    /* Ana dongu */
    while (1) {
        coap_io_process(ctx, COAP_IO_WAIT);
    }

    coap_free_context(ctx);
    coap_cleanup();
    return 0;
}

Derleme ve Test

gcc -o coap_server coap_server.c \
    $(pkg-config --cflags --libs libcoap-3) \
    -Wall -Wextra

# Sunucuyu baslatın
./coap_server &

# GET istegi test
coap-client -m get coap://localhost/sensor/temperature

# PUT ile esik guncelle
coap-client -m put -e "26.0" coap://localhost/sensor/threshold

# Observe: 30 saniye boyunca bildirimleri dinle
coap-client -m get -s 30 coap://localhost/sensor/temperature

Gözlemleme Bildirimi Tetikleme

/* Sensorden yeni deger okundigunda cagrilir */
void notify_observers(coap_context_t *ctx,
                      coap_resource_t *r_temp,
                      float new_temp)
{
    /* Onceki degerden farkli ise bildir */
    static float last_temp = -999.0f;
    if (new_temp != last_temp) {
        last_temp = new_temp;
        /* Kayitli tum gozlemcilere bildirim kuyrukla */
        coap_resource_notify_observers(r_temp, NULL);
        /* coap_io_process() bildirimleri gonderir */
    }
}

03 LwM2M Nesne Modeli

OMA LwM2M (Lightweight M2M), CoAP üzerine inşa edilmiş bir cihaz yönetim protokolüdür. Hiyerarşik nesne modeli ile cihaz bilgisinden OTA güncellemesine kadar tüm yönetim işlemleri standartlaştırılmıştır.

Object / Instance / Resource Hiyerarşisi

LwM2M Sunucu
    |
    +-- Cihaz (Bootstrap veya DM Sunucusu)
         |
         +-- Object (Nesne) -- orn. Object 3: Device
              |
              +-- Object Instance (Ornek) -- orn. /3/0
                   |
                   +-- Resource (Kaynak) -- orn. /3/0/0 = Manufacturer
                                          +-- /3/0/9  = Battery Level
                                          +-- /3/0/13 = Current Time
  

Her nesne ve kaynak OMA tarafından benzersiz bir tam sayı ID ile tanımlanır. Bu şema herhangi bir sunucunun herhangi bir uyumlu cihazı yönetmesini sağlar. LwM2M adresleme şeması /ObjectID/InstanceID/ResourceID biçimindedir:

Object IDNesne AdıAçıklama
0LwM2M SecuritySunucu bağlantı güvenlik bilgileri (PSK, RPK, X.509)
1LwM2M ServerSunucu URI, ömür (lifetime), binding modu
2Access ControlKaynak erişim hakları yönetimi
3DeviceÜretici, model, seri no, pil seviyesi, cihaz saati
4Connectivity MonitoringAğ arayüzü, IP adresi, sinyal gücü
5Firmware UpdateOTA paketi indirme ve uygulama durum makinesi
6LocationGPS enlemi, boylamı, yüksekliği, zaman damgası
3303TemperatureIPSO sensör nesnesi; mevcut değer, min, max, birim
3311Light ControlIPSO ışık kontrolü; On/Off durumu ve parlaklık

LwM2M İşlemleri

LwM2M İşlemiCoAP MetoduÖrnek
ReadGETGET /3/0/9 pil seviyesini döner
WritePUTPUT /3/0/13 zaman ayarlar
ExecutePOSTPOST /3/0/4 cihazı yeniden başlatır
ObserveGET + Observe:0GET /3303/0/5700 sıcaklığı izler
DiscoverGET + Accept:40GET /3 nesne linklerini listeler

Bootstrap Süreci

LwM2M cihazları fabrika çıkışında yalnızca Bootstrap Sunucusu bilgisiyle gelir. İlk açılışta bootstrap sunucusuna kayıt olur ve DM sunucu bilgilerini alır:

Cihaz                Bootstrap Sunucu         DM Sunucu
  |                        |                      |
  |-- Bootstrap Request -->|                      |
  |<-- Write (Object 0) ---|  (DM sunucu URI/PSK) |
  |<-- Bootstrap Finish ---|                      |
  |                        |                      |
  |-- Register (DM) ---------------------->|      |
  |<-- 2.01 Created ----------------------|      |
  |                        |                      |
  |-- Update (lifetime) ------------------>|      |
  |<-- 2.04 Changed -----------------------|      |
  

04 Wakaama İstemci Entegrasyonu

Eclipse Wakaama, gömülü Linux ve bare-metal RTOS için C dilinde yazılmış hafif bir LwM2M yığınıdır. Birkaç yüz KB bellekle çalışabilir ve cross-platform desteği sunar.

Kurulum ve Derleme

git clone https://github.com/eclipse/wakaama.git
cd wakaama

# Linux istemci ornegi derle
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j4

# Olusan binary: build/examples/lightclient/lightclient

Wakaama İstemci Yapısı — Object 3 (Device)

#include "liblwm2m.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* lwm2m_context_t tum istemci durumunu tutar */
lwm2m_context_t *lwm2mH = NULL;

/* Object 3 (Device) icin kaynak okuma */
static uint8_t
device_read(lwm2m_context_t *contextP,
            uint16_t instanceId,
            int *numDataP,
            lwm2m_data_t **dataArrayP,
            lwm2m_object_t *objectP)
{
    int i;

    /* Istenen kaynak listesi bossa tumunu doldur */
    if (*numDataP == 0) {
        /* RES 0=Manufacturer, 1=Model, 2=Serial, 9=Battery */
        uint16_t resList[] = { 0, 1, 2, 9 };
        *numDataP    = 4;
        *dataArrayP  = lwm2m_data_new(4);
        if (!*dataArrayP) return COAP_500_INTERNAL_SERVER_ERROR;
        for (i = 0; i < 4; i++)
            (*dataArrayP)[i].id = resList[i];
    }

    for (i = 0; i < *numDataP; i++) {
        switch ((*dataArrayP)[i].id) {
        case 0:
            lwm2m_data_encode_string("EmbeddedCo",
                                     &(*dataArrayP)[i]);
            break;
        case 1:
            lwm2m_data_encode_string("SensorNode-v1",
                                     &(*dataArrayP)[i]);
            break;
        case 2:
            lwm2m_data_encode_string("SN-20260001",
                                     &(*dataArrayP)[i]);
            break;
        case 9:  /* Pil seviyesi (%) */
            lwm2m_data_encode_int(87, &(*dataArrayP)[i]);
            break;
        default:
            return COAP_404_NOT_FOUND;
        }
    }
    return COAP_205_CONTENT;
}

Object 3303 (Temperature) Okuma

/* Object 3303 (Temperature) icin kaynak okuma */
static uint8_t
temperature_read(lwm2m_context_t *contextP,
                 uint16_t instanceId,
                 int *numDataP,
                 lwm2m_data_t **dataArrayP,
                 lwm2m_object_t *objectP)
{
    int i;
    if (*numDataP == 0) {
        /* 5700=Value, 5601=Min, 5602=Max, 5701=Unit */
        uint16_t resList[] = { 5700, 5601, 5602, 5701 };
        *numDataP   = 4;
        *dataArrayP = lwm2m_data_new(4);
        if (!*dataArrayP) return COAP_500_INTERNAL_SERVER_ERROR;
        for (i = 0; i < 4; i++)
            (*dataArrayP)[i].id = resList[i];
    }

    for (i = 0; i < *numDataP; i++) {
        switch ((*dataArrayP)[i].id) {
        case 5700:  /* Mevcut sicaklik degeri */
            lwm2m_data_encode_float(23.7, &(*dataArrayP)[i]);
            break;
        case 5601:  /* Min olculen deger */
            lwm2m_data_encode_float(18.2, &(*dataArrayP)[i]);
            break;
        case 5602:  /* Max olculen deger */
            lwm2m_data_encode_float(31.4, &(*dataArrayP)[i]);
            break;
        case 5701:  /* Birim dizesi */
            lwm2m_data_encode_string("Cel", &(*dataArrayP)[i]);
            break;
        default:
            return COAP_404_NOT_FOUND;
        }
    }
    return COAP_205_CONTENT;
}

/* Ana dongu */
int main(void)
{
    lwm2m_object_t *objArray[3];

    objArray[0] = get_security_object(123,
                      "coap://leshan.eclipse.org:5683",
                      NULL, NULL, 0, false);
    objArray[1] = get_server_object(123, "U", 300, false);
    objArray[2] = make_object(3, device_read, NULL, NULL);

    lwm2mH = lwm2m_init(NULL);
    lwm2m_configure(lwm2mH, "embedded-sensor-01",
                    NULL, NULL, 3, objArray);

    printf("LwM2M istemci baslatildi\n");

    while (1) {
        struct timeval tv = { 5, 0 };
        lwm2m_step(lwm2mH, &tv.tv_sec);
        /* select() ile ag olaylarini isle */
    }

    lwm2m_close(lwm2mH);
    return 0;
}

05 OTA Firmware Güncelleme

LwM2M Object 5 (Firmware Update), standart bir OTA mekanizması sağlar. Push ve Pull olmak üzere iki yöntem desteklenir; durum makinesi sunucu tarafından gerçek zamanlı izlenir.

Object 5 Kaynakları

Kaynak IDAdıAçıklama
0PackagePush yöntemi: sunucu binary'yi doğrudan Block1 ile yazar
1Package URIPull yöntemi: cihaz bu URI'dan binary'yi indirir
2Update (Execute)İndirilen paketi uygula komutu; Execute işlemi
3State0=Idle, 1=Downloading, 2=Downloaded, 3=Updating
5Update Result0=Initial, 1=Success, 2=No Flash, 3=OOM, 4=CRC Error
6PkgNameFirmware paketinin adı (opsiyonel)
7PkgVersionFirmware paketinin sürüm dizesi

OTA Durum Makinesi (Pull Yöntemi)

        Sunucu                     Cihaz
           |                         |
           |-- Write /5/0/1 (URI) -->|  (Pull yontemi)
           |                         |-- State: 1 (Downloading)
           |                         |   [HTTP/CoAP Block2 ile indir]
           |                         |-- State: 2 (Downloaded)
           |                         |   [CRC dogrula]
           |-- Execute /5/0/2 ------>|
           |                         |-- State: 3 (Updating)
           |                         |   [swupdate / mtd_write]
           |                         |   [reboot]
           |                         |
           |      [Cihaz yeniden baslar]
           |                         |
           |<-- Register (yeni ver) --|  Update Result: 1 (Success)
  

Pull Yöntemi — C Uygulama Taslağı

/* Object 5 durum makinesi sabitleri */
#define FIRMWARE_STATE_IDLE        0
#define FIRMWARE_STATE_DOWNLOADING 1
#define FIRMWARE_STATE_DOWNLOADED  2
#define FIRMWARE_STATE_UPDATING    3

static int fw_state  = FIRMWARE_STATE_IDLE;
static int fw_result = 0;

/* Object 5 / Resource 1 yazildiginda cagrilir */
static uint8_t
firmware_write(lwm2m_context_t *ctx,
               uint16_t instanceId,
               int numData,
               lwm2m_data_t *dataArray,
               lwm2m_object_t *objectP)
{
    int i;
    for (i = 0; i < numData; i++) {
        if (dataArray[i].id == 1) { /* Package URI */
            const char *uri =
                (const char *)dataArray[i].value.asBuffer.buffer;
            printf("Firmware URI alindi: %s\n", uri);
            fw_state = FIRMWARE_STATE_DOWNLOADING;
            /* Arka planda indirme baslatilir */
            start_firmware_download(uri);
        }
    }
    return COAP_204_CHANGED;
}

/* Indirme tamamlandiginda cagrilir */
void on_firmware_downloaded(const char *path, uint32_t crc32)
{
    if (verify_crc32(path, crc32)) {
        fw_state = FIRMWARE_STATE_DOWNLOADED;
        printf("Firmware dogrulandi, guncelleme bekleniyor\n");
    } else {
        fw_result = 4; /* CRC hatasi */
        fw_state  = FIRMWARE_STATE_IDLE;
    }
}

/* Execute /5/0/2 alindigi zaman cagrilir */
static uint8_t
firmware_execute(lwm2m_context_t *ctx,
                 uint16_t instanceId,
                 lwm2m_data_t *dataArray,
                 lwm2m_object_t *objectP)
{
    if (fw_state != FIRMWARE_STATE_DOWNLOADED)
        return COAP_405_METHOD_NOT_ALLOWED;

    fw_state = FIRMWARE_STATE_UPDATING;

    /* SWUpdate ile guncelle, reboot ile tamamla */
    system("swupdate -i /tmp/firmware.swu -e stable,copy2 && reboot");
    return COAP_204_CHANGED;
}

06 DTLS ile Güvenlik

CoAP ve LwM2M, güvenlik katmanı olarak DTLS (Datagram TLS) kullanır. tinydtls veya mbedTLS ile entegre edilerek kimlik doğrulama ve şifreleme sağlanır.

DTLS Kimlik Doğrulama Modları

PSK (Pre-Shared Key)Önceden paylaşılan anahtar; kısıtlı cihazlar için en basit yöntem, en az bellek kullanır (~4 KB RAM)
RPK (Raw Public Key)Sertifika zinciri olmadan ham açık anahtar; PKI altyapısına gerek kalmaz (~8 KB RAM)
X.509Tam sertifika zinciri; kurumsal PKI entegrasyonu için kullanılır (~16 KB RAM)

LwM2M Object 0 ile PSK Yapılandırması

/* Object 0 (Security) PSK ornegi */
lwm2m_object_t *get_security_object_psk(
        uint16_t serverId,
        const char *serverUri,
        const char *pskId,     /* kimlik dizesi */
        const uint8_t *psk,    /* ikili anahtar */
        uint16_t pskLen)
{
    lwm2m_object_t *secObj = lwm2m_malloc(sizeof(lwm2m_object_t));
    memset(secObj, 0, sizeof(lwm2m_object_t));
    secObj->objID = LWM2M_SECURITY_OBJECT_ID;  /* 0 */

    security_instance_t *inst =
        lwm2m_malloc(sizeof(security_instance_t));
    memset(inst, 0, sizeof(security_instance_t));

    inst->instanceId   = 0;
    inst->uri          = lwm2m_strdup(serverUri);
    inst->securityMode = LWM2M_SECURITY_MODE_PRE_SHARED_KEY;

    /* PSK kimlik ve anahtar */
    inst->publicIdentity   = lwm2m_strdup(pskId);
    inst->publicIdLen      = strlen(pskId);
    inst->secretKey        = lwm2m_malloc(pskLen);
    memcpy(inst->secretKey, psk, pskLen);
    inst->secretKeyLen     = pskLen;
    inst->shortID          = serverId;
    inst->isBootstrap      = false;

    secObj->instanceList = LWM2M_LIST_ADD(NULL, inst);
    secObj->readFunc     = security_read;
    secObj->writeFunc    = security_write;

    return secObj;
}

/*
 Kullanim ornegi:
   uint8_t psk[] = { 0x73, 0x65, 0x63, 0x72, 0x65, 0x74 };
   obj = get_security_object_psk(
             123,
             "coaps://leshan.eclipse.org:5684",
             "device-identity-01",
             psk, sizeof(psk));
*/

tinydtls ile libcoap Entegrasyonu

# libcoap'i tinydtls ile derle
./configure --enable-dtls --with-tinydtls
make

# Guvenli CoAP sunucu baslatma (PSK modu)
coap-server -k "gizlianahtar" -h "kimlik01" -p 5684

# Guvenli istemci istegi
coap-client -m get coaps://localhost/sensor/temperature \
            -k "gizlianahtar" -h "kimlik01"

DTLS El Sıkışma Akışı

Istemci                    Sunucu
   |                          |
   |-- ClientHello ---------->|
   |<-- HelloVerifyRequest ----|  (Anti-DoS cookie)
   |-- ClientHello (cookie) -->|
   |<-- ServerHello -----------|
   |<-- ServerKeyExchange -----|  (PSK: hint gonderilir)
   |<-- ServerHelloDone -------|
   |-- ClientKeyExchange ---->|  (PSK: identity gonderilir)
   |-- ChangeCipherSpec ----->|
   |-- Finished ------------->|
   |<-- ChangeCipherSpec ------|
   |<-- Finished --------------|
   |=== Sifreli CoAP veri =====|
  

Cipher Suite Seçimi (Gömülü İçin)

Cipher SuiteÖzellikGömülü Uygunluk
TLS_PSK_WITH_AES_128_CCM_8PSK + AES-CCM-8En hafif; kısıtlı cihazlar için önerilir
TLS_PSK_WITH_AES_128_CBC_SHA256PSK + CBC-SHA256Geniş destek; biraz daha fazla yük
TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8ECDHE + X.509Kurumsal; mbedTLS ile 50+ KB ROM

07 Gömülü Senaryo: Raspberry Pi Sıcaklık Sensörü

Raspberry Pi üzerinde DS18B20 sıcaklık sensörü, Wakaama LwM2M istemcisi ve Eclipse Leshan sunucusu ile uçtan uca bir IoT cihaz yönetim senaryosu kurulur.

Sistem Mimarisi

[DS18B20 Sensor]
      |
      | 1-Wire (GPIO4)
      v
[Raspberry Pi 4 -- Linux 6.6]
  - Wakaama istemci (LwM2M 1.1)
  - libcoap + tinydtls
  - IPSO Object 3303 (Temperature)
      |
      | CoAPS (UDP 5684) / LTE veya Wi-Fi
      v
[Eclipse Leshan Sunucu -- Java]
  - REST API + Web UI
  - Nesne izleme / OTA tetikleme
      |
      v
[OTA Deposu -- SWUpdate imzali]
  - SHA256 + RSA-2048 imzali swu paketi
  

DS18B20 Okuma — Linux sysfs

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* DS18B20 sensoru /sys/bus/w1/devices/ altinda gorunur */
float read_ds18b20(const char *sensor_id)
{
    char path[128];
    char buf[256];
    FILE *fp;
    float temp = -999.0f;

    snprintf(path, sizeof(path),
             "/sys/bus/w1/devices/%s/w1_slave", sensor_id);

    fp = fopen(path, "r");
    if (!fp) { perror("Sensor acilamadi"); return temp; }

    /* Birinci satir: CRC kontrolu (YES/NO) */
    if (fgets(buf, sizeof(buf), fp) == NULL) goto done;
    if (strstr(buf, "YES") == NULL) goto done;

    /* Ikinci satir: "t=XXXXX" formatinda milisantigrat */
    if (fgets(buf, sizeof(buf), fp) == NULL) goto done;
    {
        char *t_pos = strstr(buf, "t=");
        if (t_pos) {
            long raw = atol(t_pos + 2);
            temp = raw / 1000.0f;
        }
    }
done:
    fclose(fp);
    return temp;
}

/*
 LwM2M Object 3303 ile entegrasyon:
   float sicaklik = read_ds18b20("28-01234567abcd");
   lwm2m_data_encode_float(sicaklik, data);
*/

Yocto ile Sistem İmajı

# local.conf
MACHINE = "raspberrypi4-64"
IMAGE_INSTALL:append = " wakaama-client libcoap tinydtls \
                          kernel-module-w1-gpio \
                          kernel-module-w1-therm"

# 1-Wire GPIO4 etkinlestirmek icin Device Tree overlay
KERNEL_DEVICETREE:append = " overlays/w1-gpio.dtbo"

# meta-raspberrypi katmani RPI_EXTRA_CONFIG icin:
# RPI_EXTRA_CONFIG += "dtoverlay=w1-gpio"

Leshan Sunucusu — OTA Tetikleme (REST API)

# Object 5 / Instance 0 / Resource 1 yaz (Package URI)
curl -X PUT \
  http://leshan-server:8080/api/clients/embedded-sensor-01/5/0/1 \
  -H "Content-Type: application/json" \
  -d '{"id":1,"kind":"singleResource","value":"coap://ota.local/firmware-v1.2.swu"}'

# Durum sorgula (State: 0-3)
curl http://leshan-server:8080/api/clients/embedded-sensor-01/5/0/3

# Guncellemeyi baslat (Execute /5/0/2)
curl -X POST \
  http://leshan-server:8080/api/clients/embedded-sensor-01/5/0/2

# Guncelleme sonucunu sorgula (1 = basarili)
curl http://leshan-server:8080/api/clients/embedded-sensor-01/5/0/5

08 Hata Ayıklama Teknikleri

CoAP ve LwM2M katmanlarında sorunları teşhis etmek için komut satırı araçları, Wireshark eklentisi ve Leshan web arayüzü birlikte kullanılır.

coap-client CLI ile Hızlı Test

# GET istegi
coap-client -m get coap://192.168.1.100:5683/sensor/temperature

# Observe modu: 10 saniye boyunca bildirimleri al
coap-client -m get -s 10 coap://192.168.1.100:5683/sensor/temperature

# PUT ile deger gonder
coap-client -m put -e "25.0" coap://192.168.1.100:5683/sensor/threshold

# POST ile kaynak olustur
coap-client -m post \
  -t application/json \
  -e '{"name":"sensor1","location":"room1"}' \
  coap://192.168.1.100:5683/devices

# Verbose mod (paket ayrinti, seviye 0-9)
coap-client -v 7 -m get coap://192.168.1.100:5683/sensor/temperature

# DTLS ile guvenli istek
coap-client -m get -k "psk_key" -h "client_identity" \
  coaps://192.168.1.100:5684/sensor/temperature

# Kaynaklari kesif (well-known/core)
coap-client -m get coap://192.168.1.100:5683/.well-known/core

Wireshark CoAP Dissector

# Wireshark 3.6+ CoAP dissector varsayilan aktiftir
# 5683 portunu filtrele:
udp.port == 5683

# Yalnizca GET istekleri (Code = 0.01):
coap.code == 1

# Yalnizca 2.05 Content yanitlari:
coap.code == 69

# Belirli token ile eslestir:
coap.token == ab:cd:ef:01

# DTLS sifre cozme (PSK modunda):
# Edit > Preferences > Protocols > DTLS > PSK Keys
# Format: IP,Port,Protocol,Key_hex

# tcpdump ile trafik yakala
tcpdump -i eth0 -w coap.pcap udp port 5683 or udp port 5684

Leshan UI ile Nesne İzleme

Eclipse Leshan web arayüzü (varsayılan port 8080) üzerinden kayıtlı cihazlar, nesneleri ve kaynakları gerçek zamanlı izlenebilir:

Clients sekmesiTüm kayıtlı LwM2M istemcilerini, son kayıt zamanını ve yaşam süresini gösterir
Nesne ağacıCihazın desteklediği tüm Object/Instance/Resource hiyerarşisini görsel olarak sunar
Observe butonuHerhangi bir kaynağa observe aboneliği oluşturur; değer değişimlerini anlık gösterir
Write/ExecuteDoğrudan kaynak güncelleme ve komut yürütme işlemlerini UI üzerinden gerçekleştirir
Event logTüm CoAP mesaj alışverişlerini, durum geçişlerini ve hata kodlarını kaydeder

Wakaama Debug Logları

# Wakaama debug modu ile derle
cmake .. -DCMAKE_BUILD_TYPE=Debug -DLWM2M_WITH_LOGS=ON

# Ornek log ciktisi:
# [INFO] lwm2m_step() called
# [INFO] State: STATE_REGISTERING
# [INFO] Sending REGISTER to coap://leshan:5683
# [DEBUG] Message ID: 0x4A2B, Token: 0xDEADBEEF
# [INFO] Registration successful, location: /rd/abc123
# [INFO] Lifetime: 300s, next update in 270s

Yaygın Sorunlar ve Çözümleri

SorunOlası NedenÇözüm
Kayıt başarısız, 4.01 UnauthorizedPSK anahtar uyumsuzluğuObject 0 kaynaklarını doğrula; hex dump karşılaştır
CON mesajı sürekli yeniden gönderiliyorGüvenlik duvarı UDP 5683'ü engelliyoriptables -I INPUT -p udp --dport 5683 -j ACCEPT
Block2 transferi yarıda kesiliyorMTU uyumsuzluğu, 6LoWPAN fragmentasyonuCOAP_MAX_BLOCK_SZX = 4 (256 bayt) olarak küçült
Observe bildirimleri gelmiyorNAT UDP oturumunu düşürüyorCoAP PING ile oturumu canlı tut; NON moduna geç
Firmware State donmuşUpdate Result set edilmemişGüncelleme handler'ında result ve state değerlerini ayarla