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:
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.
| Özellik | HTTP/1.1 | CoAP |
|---|---|---|
| Taşıma katmanı | TCP | UDP |
| Başlık boyutu | 200-500 bayt | 4 bayt sabit |
| Güvenlik | TLS | DTLS |
| Multicast | Desteklenmez | NON mesajlarıyla desteklenir |
| Gözlemleme (push) | SSE/WebSocket gerekir | Yerleşik Observe seçeneği |
| İkili kodlama | Metin | İ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
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 / Kod | Açıklama |
|---|---|
| GET | Kaynağı okur; gözlem için Observe seçeneği eklenir |
| POST | Kaynak oluşturur veya mevcut kaynağa işlem uygular |
| PUT | Kaynağı tamamen günceller |
| DELETE | Kaynağı siler |
| 2.05 Content | GET isteğine başarılı yanıt (HTTP 200 eşdeğeri) |
| 2.01 Created | POST ile kaynak oluşturuldu (HTTP 201) |
| 4.04 Not Found | Kaynak bulunamadı (HTTP 404) |
| 5.00 Internal Server Error | Sunucu 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:
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 ID | Nesne Adı | Açıklama |
|---|---|---|
| 0 | LwM2M Security | Sunucu bağlantı güvenlik bilgileri (PSK, RPK, X.509) |
| 1 | LwM2M Server | Sunucu URI, ömür (lifetime), binding modu |
| 2 | Access Control | Kaynak erişim hakları yönetimi |
| 3 | Device | Üretici, model, seri no, pil seviyesi, cihaz saati |
| 4 | Connectivity Monitoring | Ağ arayüzü, IP adresi, sinyal gücü |
| 5 | Firmware Update | OTA paketi indirme ve uygulama durum makinesi |
| 6 | Location | GPS enlemi, boylamı, yüksekliği, zaman damgası |
| 3303 | Temperature | IPSO sensör nesnesi; mevcut değer, min, max, birim |
| 3311 | Light Control | IPSO ışık kontrolü; On/Off durumu ve parlaklık |
LwM2M İşlemleri
| LwM2M İşlemi | CoAP Metodu | Örnek |
|---|---|---|
| Read | GET | GET /3/0/9 pil seviyesini döner |
| Write | PUT | PUT /3/0/13 zaman ayarlar |
| Execute | POST | POST /3/0/4 cihazı yeniden başlatır |
| Observe | GET + Observe:0 | GET /3303/0/5700 sıcaklığı izler |
| Discover | GET + Accept:40 | GET /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 ID | Adı | Açıklama |
|---|---|---|
| 0 | Package | Push yöntemi: sunucu binary'yi doğrudan Block1 ile yazar |
| 1 | Package URI | Pull yöntemi: cihaz bu URI'dan binary'yi indirir |
| 2 | Update (Execute) | İndirilen paketi uygula komutu; Execute işlemi |
| 3 | State | 0=Idle, 1=Downloading, 2=Downloaded, 3=Updating |
| 5 | Update Result | 0=Initial, 1=Success, 2=No Flash, 3=OOM, 4=CRC Error |
| 6 | PkgName | Firmware paketinin adı (opsiyonel) |
| 7 | PkgVersion | Firmware 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ı
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 | Özellik | Gömülü Uygunluk |
|---|---|---|
| TLS_PSK_WITH_AES_128_CCM_8 | PSK + AES-CCM-8 | En hafif; kısıtlı cihazlar için önerilir |
| TLS_PSK_WITH_AES_128_CBC_SHA256 | PSK + CBC-SHA256 | Geniş destek; biraz daha fazla yük |
| TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 | ECDHE + X.509 | Kurumsal; 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:
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
| Sorun | Olası Neden | Çözüm |
|---|---|---|
| Kayıt başarısız, 4.01 Unauthorized | PSK anahtar uyumsuzluğu | Object 0 kaynaklarını doğrula; hex dump karşılaştır |
| CON mesajı sürekli yeniden gönderiliyor | Güvenlik duvarı UDP 5683'ü engelliyor | iptables -I INPUT -p udp --dport 5683 -j ACCEPT |
| Block2 transferi yarıda kesiliyor | MTU uyumsuzluğu, 6LoWPAN fragmentasyonu | COAP_MAX_BLOCK_SZX = 4 (256 bayt) olarak küçült |
| Observe bildirimleri gelmiyor | NAT UDP oturumunu düşürüyor | CoAP 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 |