00 CAN Tanılama Temelleri
Modern araçlarda yüzlerce elektronik kontrol ünitesi (ECU) CAN ağı üzerinden haberleşir. Bu ECU'ların tanılanması, kalibre edilmesi ve güncellenmesi için standartlaştırılmış protokollere ihtiyaç vardır. OBD-II, ISO-TP ve UDS bu standartlar zincirinin temel halkalarıdır.
8 Bayt Sınırı ve ISO-TP Neden Gerekli?
Klasik CAN çerçevesi en fazla 8 bayt veri taşıyabilir. Firmware güncellemesi, arıza kodu listesi veya kalibrasyon verisi gibi büyük veri blokları bu sınır dahilinde iletilemez. ISO 15765-2 (ISO-TP) bu sorunu çözer: CAN çerçevelerini birleştirerek 4095 bayta kadar veri transferini mümkün kılar.
+---------+ +-----------+ +----------+
| OBD-II | | UDS | | Özel |
| SAE | | ISO 14229| | Protokol |
| J1979 | | | | |
+---------+ +-----------+ +----------+
| | |
+--------------------+----------------------+
| Uygulama Katmanı
+--------+--------+
| ISO-TP |
| ISO 15765-2 | Taşıma Katmanı
+--------+--------+
|
+-------------+-------------+
| CAN 2.0A / 2.0B |
| 11-bit / 29-bit ID | Veri Bağı
+---------------------------+
Protokol Ailesi ve Standartlar
| Standart | Ad | Açıklama |
|---|---|---|
| ISO 15765-2 | ISO-TP | CAN üzerinde çok çerçeveli mesaj taşıma (maks. 4095 bayt) |
| ISO 14229 | UDS | Unified Diagnostic Services — ECU tanılama ve programlama |
| SAE J1979 | OBD-II | Teşhis PID'leri — motor, emisyon, sensör verileri |
| ISO 13400 | DoIP | Diagnostics over IP — Ethernet tabanlı UDS taşıma |
| ISO 14230 | KWP2000 | Eski araçlarda K-Line ve CAN üzerinde tanılama |
| ISO 11898 | CAN | CAN fiziksel ve veri bağı katmanı |
Linux SocketCAN Ekosistemi
Linux, CAN arayüzlerini standart ağ arayüzleri (örn. can0) olarak yönetir.
AF_CAN soket ailesi üzerinden ham CAN, ISO-TP ve J1939 paketleri alınıp gönderilebilir.
# SocketCAN arayüzünü 500 kbit/s ile başlat
ip link set can0 type can bitrate 500000
ip link set can0 up
# Durum kontrol
ip -details link show can0
# Tüm CAN trafiğini izle
candump can0
# Basit OBD-II sorgusu: motor soğutma suyu sıcaklığı
# ID=0x7DF (broadcast), 3 bayt: Mode 01, PID 0x05
cansend can0 7DF#0301050000000000
OBD-II Konnektör Pinout (DLC)
| Pin | Sinyal | Açıklama |
|---|---|---|
| 4 | GND (Chassis) | Şasi toprağı |
| 5 | GND (Signal) | Sinyal toprağı |
| 6 | CAN-H | Yüksek hız CAN yüksek hattı (500 kbit/s) |
| 14 | CAN-L | Yüksek hız CAN düşük hattı |
| 16 | VBAT | Araç aküsü (12 V) — tanılama araçlarını besler |
01 ISO-TP Protokolü
ISO 15765-2 (ISO-TP), 8 baytlık CAN çerçevelerini birleştirerek 4095 bayta kadar veri iletimi sağlar. Dört çerçeve tipi tanımlar: Single Frame, First Frame, Consecutive Frame ve Flow Control Frame.
Single Frame (SF) — Tek Çerçeve Mesaj
Mesaj 7 bayt veya daha kısaysa tek bir CAN çerçevesine sığar:
| Bayt | Alan | Değer | Açıklama |
|---|---|---|---|
| 0 [7:4] | PCI Type | 0x0 | Single Frame tipi göstergesi |
| 0 [3:0] | SF_DL | 1–7 | Mesaj verisi uzunluğu (bayt) |
| 1–7 | Data | — | Uygulama verisi |
First Frame (FF) ve Consecutive Frame (CF)
Mesaj 7 bayttan büyükse önce First Frame gönderilir, ardından Flow Control beklenir, sonra Consecutive Frame'ler sırayla iletilir:
| Çerçeve | Byte 0 [7:4] | Temel Alanlar | Açıklama |
|---|---|---|---|
| Single Frame (SF) | 0x0 | SF_DL[3:0], Data[1-7] | Kısa mesaj — tek çerçeve |
| First Frame (FF) | 0x1 | FF_DL[11:0], Data[2-7] | Uzun mesajın başlangıcı — toplam uzunluğu bildirir |
| Consecutive Frame (CF) | 0x2 | SN[3:0], Data[1-7] | Ardışık veri parçaları — SN 0x1–0xF döngüsel |
| Flow Control (FC) | 0x3 | FS[1:0], BS, STmin | Alıcı hız kontrolü — Block Size ve minimum aralık |
Çok Çerçeve İletişim Akışı
Talep Eden (Tester) Yanıt Veren (ECU)
| |
|--- First Frame (FF) -----------> | Toplam: 100 bayt
| |
|<-- Flow Control (FC) ----------- | BS=5, STmin=10 ms
| |
|--- Consecutive Frame SN=1 ------> |
|--- Consecutive Frame SN=2 ------> |
|--- Consecutive Frame SN=3 ------> |
|--- Consecutive Frame SN=4 ------> |
|--- Consecutive Frame SN=5 ------> | Block doldu
| |
|<-- Flow Control (FC) ----------- | BS=0 (tümünü gönder)
| |
|--- Consecutive Frame SN=6 ------> |
|--- ... (kalan CF'ler) ----------> |
Flow Control Frame Alanları
Segment Birleştirme Mekanizması
ISO-TP alıcı taraf şu adımları izler: FF alınır ve toplam mesaj uzunluğu kayıt edilir,
FC gönderilir, CF'ler SN sırasına göre birleştirilir, tüm baytlar alındığında tam
PDU (Protocol Data Unit) üst katmana teslim edilir. Linux kernel'in can_isotp
modülü bu birleştirme işlemini otomatik olarak gerçekleştirir.
02 Linux ISO-TP Soketi
Linux 5.10 ve sonrasında çekirdek içi ISO-TP soket desteği mevcuttur. Kullanıcı alanı
uygulamaları AF_CAN / SOCK_DGRAM / CAN_ISOTP üçlüsüyle kernel'in çerçeveleme
katmanını kullanabilir.
Kernel Modülü ve Bağımlılıklar
# ISO-TP modülünü yükle
modprobe can
modprobe can_raw
modprobe can_isotp
# Modül yüklü mü?
lsmod | grep isotp
# can_isotp 32768 0
# Kernel config kontrolü
zcat /proc/config.gz | grep CONFIG_CAN_ISOTP
# CONFIG_CAN_ISOTP=m
# can0 arayüzünü başlat
ip link set can0 type can bitrate 500000
ip link set can0 up
sockaddr_can Yapısı — ISO-TP Adresleme
/* linux/can/isotp.h — temel adres yapısı */
struct sockaddr_can {
__kernel_sa_family_t can_family; /* AF_CAN */
int can_ifindex; /* arayuz indeksi (can0) */
union {
struct { canid_t rx_id, tx_id; } tp; /* ISO-TP ID'leri */
/* ... diger protokoller */
} can_addr;
};
/* Tipik OBD-II ID atamalari */
/* tx_id = 0x7DF (OBD-II broadcast tester ID) */
/* rx_id = 0x7E8 (ECU 1 yanit ID) */
/* rx_id = 0x7E9 (ECU 2 yanit ID) vb. */
can_isotp_fc_opts — Flow Control Parametreleri
ISO-TP Soket Seçenekleri
| Soket Seçeneği | Yapı | Açıklama |
|---|---|---|
CAN_ISOTP_OPTS | can_isotp_options | Bayraklar: padding, extend addr, half-duplex |
CAN_ISOTP_RECV_FC | can_isotp_fc_options | FC gönderme parametreleri: BS, STmin, WFTmax |
CAN_ISOTP_TX_STMIN | uint32_t | TX tarafı minimum çerçeve aralığı (µs) |
CAN_ISOTP_RX_STMIN | uint32_t | RX tarafı minimum CF aralığı — bu süreden önce gelen CF atılır |
CAN_ISOTP_LL_OPTS | can_isotp_ll_options | CAN FD / BRS için link layer seçenekleri |
Önemli can_isotp_options Bayrakları
| Bayrak | Değer | Açıklama |
|---|---|---|
CAN_ISOTP_LISTEN_MODE | 0x001 | Pasif dinleme — FC göndermez, izleme için |
CAN_ISOTP_EXTEND_ADDR | 0x002 | Genişletilmiş adres (Byte 0 adres alanı) |
CAN_ISOTP_TX_PADDING | 0x004 | TX çerçevelerini 8 bayta doldur |
CAN_ISOTP_RX_PADDING | 0x008 | RX padding doğrulamasını etkinleştir |
CAN_ISOTP_HALF_DUPLEX | 0x040 | Yarı çift yön modu (aynı ID TX/RX) |
03 Temel ISO-TP Uygulaması (C)
Linux kernel ISO-TP soket API'si üzerinden C diliyle tam bir ISO-TP istemci örneği. Soket oluşturma, arayüz bağlama, veri gönderme ve almayi kapsar.
Temel ISO-TP Soket Örneği
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/isotp.h>
#include <net/if.h>
#include <sys/ioctl.h>
#define CAN_IFACE "can0"
#define TESTER_ID 0x7DF /* OBD-II broadcast tester ID */
#define ECU_ID 0x7E8 /* ECU yakit ID */
int isotp_open(const char *iface, canid_t tx_id, canid_t rx_id)
{
int sock;
struct sockaddr_can addr;
struct ifreq ifr;
/* 1) SOCK_DGRAM + CAN_ISOTP soketi olustur */
sock = socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP);
if (sock < 0) {
perror("socket");
return -1;
}
/* 2) Arayuz indeksini al */
strncpy(ifr.ifr_name, iface, IFNAMSIZ - 1);
if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {
perror("ioctl SIOCGIFINDEX");
close(sock);
return -1;
}
/* 3) Adres yapisini doldur */
memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
addr.can_addr.tp.tx_id = tx_id;
addr.can_addr.tp.rx_id = rx_id;
/* 4) Soketi bagla */
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("bind");
close(sock);
return -1;
}
return sock;
}
int main(void)
{
int sock;
unsigned char buf[4096];
ssize_t nbytes;
/* Soket ac */
sock = isotp_open(CAN_IFACE, TESTER_ID, ECU_ID);
if (sock < 0)
return 1;
/* FC parametrelerini ayarla */
struct can_isotp_fc_options fc = {0};
fc.bs = 0; /* sinırsız blok */
fc.stmin = 0; /* bekleme yok */
setsockopt(sock, SOL_CAN_ISOTP, CAN_ISOTP_RECV_FC, &fc, sizeof(fc));
/* TX padding etkinlestir (bazi ECU'lar zorunlu kilar) */
struct can_isotp_options opts = {0};
opts.flags = CAN_ISOTP_TX_PADDING;
opts.txpad_content = 0xCC;
setsockopt(sock, SOL_CAN_ISOTP, CAN_ISOTP_OPTS, &opts, sizeof(opts));
/* UDS ReadDataByIdentifier 0x22 0xF1 0x90 (VIN) gonder */
unsigned char req[] = { 0x22, 0xF1, 0x90 };
if (write(sock, req, sizeof(req)) < 0) {
perror("write");
close(sock);
return 1;
}
printf("Istek gonderildi: 22 F1 90\n");
/* Yaniti oku — ISO-TP otomatik birlestirme yapar */
nbytes = read(sock, buf, sizeof(buf));
if (nbytes < 0) {
perror("read");
} else {
printf("Yanit (%zd bayt):", nbytes);
for (ssize_t i = 0; i < nbytes; i++)
printf(" %02X", buf[i]);
printf("\n");
/* Pozitif yanitsa: 62 F1 90 [VIN 17 karakter] */
}
close(sock);
return 0;
}
Derleme ve Çalıştırma
# Derleme (cross-compile ARM64 icin)
aarch64-linux-gnu-gcc -o isotp_client isotp_client.c
# can0 hazirla
sudo modprobe can_isotp
sudo ip link set can0 type can bitrate 500000
sudo ip link set can0 up
# Calistir
./isotp_client
# Istek gonderildi: 22 F1 90
# Yanit (20 bayt): 62 F1 90 57 41 55 5A 5A 5A ... (VIN)
Zaman Aşımı ile Güvenli Okuma
#include <sys/select.h>
/* 2 saniye zaman asimi ile yanit bekleme */
int isotp_recv_timeout(int sock, unsigned char *buf, size_t len, int timeout_sec)
{
fd_set rfds;
struct timeval tv;
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
tv.tv_sec = timeout_sec;
tv.tv_usec = 0;
int ret = select(sock + 1, &rfds, NULL, NULL, &tv);
if (ret == 0) {
fprintf(stderr, "Zaman asimi: ECU'dan yanit gelmedi\n");
return -1;
}
if (ret < 0) {
perror("select");
return -1;
}
return (int)read(sock, buf, len);
}
isotpsend / isotprecv Araçları
# can-utils paketi gerekli
apt-get install can-utils
# Tek payload gonder (ISO-TP cercevelemesi otomatik)
isotpsend -s 7DF -d 7E8 can0 <<< "22 F1 90"
# Yanit bekle (arka planda)
isotprecv -s 7E8 -d 7DF can0
# ISO-TP trafik izleme
isotpdump -s 7DF -d 7E8 can0
04 UDS Servis Katmanı
UDS (ISO 14229 — Unified Diagnostic Services), ECU tanılama, kalibrasyon, veri okuma/yazma ve firmware güncelleme işlemlerini kapsamlı bir servis seti olarak tanımlar. Her servis bir Servis Tanımlayıcısı (SID) ile adreslenır.
Temel UDS Servisleri
| SID | Servis Adı | Açıklama |
|---|---|---|
| 0x10 | DiagnosticSessionControl | Oturum modu geçişi: Default, Extended, Programming |
| 0x11 | ECUReset | ECU yeniden başlatma: Hard, Soft, KeyOffOn |
| 0x14 | ClearDiagnosticInformation | DTC kayıtlarını sil |
| 0x19 | ReadDTCInformation | DTC listesi ve durum bilgisi oku |
| 0x22 | ReadDataByIdentifier | DID ile veri oku — VIN, sensör değerleri, kalibrasyon |
| 0x23 | ReadMemoryByAddress | Belirli bellek adresinden ham veri oku |
| 0x27 | SecurityAccess | Güvenlik kilidi açma — seed/key mekanizması |
| 0x28 | CommunicationControl | Normal/NM mesaj gönderimini etkinleştir/devre dışı bırak |
| 0x2E | WriteDataByIdentifier | DID ile veri yaz — kalibrasyon parametreleri |
| 0x2F | InputOutputControlByIdentifier | I/O aktivasyonu: sensör simülasyonu, aktüatör kontrolü |
| 0x31 | RoutineControl | Rutin başlat/durdur/sonuç al — test ve doğrulama rutinleri |
| 0x34 | RequestDownload | Firmware/veri yükleme başlatma |
| 0x36 | TransferData | Veri bloğu transferi (indirme veya yükleme) |
| 0x37 | RequestTransferExit | Transfer tamamlama ve bütünlük doğrulama |
| 0x3E | TesterPresent | Oturumu canlı tut — zaman aşımını engelle |
| 0x85 | ControlDTCSetting | DTC kayıt mekanizmasını etkinleştir/devre dışı bırak |
UDS Oturum Modları
| Sub-fn | Oturum Modu | Erişilebilir Başlıca Servisler |
|---|---|---|
| 0x01 | DefaultSession | 0x10, 0x11, 0x19, 0x22, 0x3E — temel okuma |
| 0x02 | ProgrammingSession | 0x10, 0x27, 0x34, 0x36, 0x37 — firmware güncelleme |
| 0x03 | ExtendedDiagnosticSession | Tüm servisler + 0x2E, 0x2F, 0x31, 0x85 |
UDS İstek / Yanıt Formatı
/* İstek: [SID] [sub-function / DID / veri] */
/* Pozitif yanit: [SID+0x40] [echo / dondurülen veri] */
/* Negatif yanit: [0x7F] [SID] [NRC kodu] */
/* Ornek: DiagnosticSessionControl -> ExtendedSession */
/* Istek: 10 03 */
/* Yanitı: 50 03 [P2_hi] [P2_lo] [P2star_hi] [P2star_lo] */
/* Ornek: ReadDataByIdentifier VIN (DID 0xF190) */
/* Istek: 22 F1 90 */
/* Yanitı: 62 F1 90 57 41 55 5A 5A 5A ... (17 karakter) */
/* Ornek: Negatif yanit */
/* 7F 22 31 > 0x7F=NRC, SID=0x22, NRC=0x31 (OutOfRange) */
Yaygın NRC Kodları
| NRC | Ad | Açıklama |
|---|---|---|
| 0x10 | generalReject | Genel red |
| 0x11 | serviceNotSupported | Bu SID desteklenmiyor |
| 0x13 | incorrectMessageLength | Mesaj uzunluğu veya format hatalı |
| 0x22 | conditionsNotCorrect | Koşullar sağlanmamış (yanlış oturum, güç modu vb.) |
| 0x24 | requestSequenceError | Hatalı sıralama (SecurityAccess adımı atlandı) |
| 0x31 | requestOutOfRange | DID veya adres aralık dışı |
| 0x33 | securityAccessDenied | Güvenlik kilidi açılmamış |
| 0x35 | invalidKey | SecurityAccess key yanlış |
| 0x36 | exceededNumberOfAttempts | Başarısız giriş denemesi sayısı aşıldı — ECU geçici kilitli |
| 0x78 | requestCorrectlyReceived-ResponsePending | ECU meşgul, yanıt bekleniyor — TesterPresent göndermeye devam edin |
05 UDS İstemci Yazımı (C)
ISO-TP soket üzerine inşa edilmiş UDS istemcisi: oturum açma, DID okuma ve negatif yanıt kodu işleme işlevlerini içeren tam C örneği.
UDS Yardımcı Fonksiyonları
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#define UDS_POS_RESP_OFFSET 0x40
#define UDS_NRC_SID 0x7F
/* UDS isteği gönder ve yanıt al */
/* sock : açık ISO-TP soketi */
/* req : istek baytları (SID dahil) */
/* req_len : istek uzunlugu */
/* resp : yanıt tamponu */
/* resp_max : tampon boyutu */
/* dönüş: alınan bayt sayısı, hata durumunda -1 */
int uds_request(int sock,
const unsigned char *req, int req_len,
unsigned char *resp, int resp_max)
{
if (write(sock, req, req_len) < 0) {
perror("uds_request: write");
return -1;
}
/* NRC 0x78 (ResponsePending) gelirse tekrar bekle */
int total_wait = 0;
while (total_wait < 5000) { /* max 5 saniye */
int n = (int)read(sock, resp, resp_max);
if (n < 0) {
perror("uds_request: read");
return -1;
}
if (n >= 3 && resp[0] == UDS_NRC_SID && resp[2] == 0x78) {
/* ECU mesgul — 100 ms bekle, tekrar dene */
usleep(100000);
total_wait += 100;
continue;
}
return n;
}
fprintf(stderr, "uds_request: ResponsePending zaman asimi\n");
return -1;
}
/* Negatif yanit kontrolu */
int uds_check_response(const unsigned char *resp, int len, unsigned char sid)
{
if (len < 1) return -1;
/* Negatif yanit */
if (resp[0] == UDS_NRC_SID) {
if (len >= 3) {
fprintf(stderr, "NRC hatasi: SID=0x%02X NRC=0x%02X\n",
resp[1], resp[2]);
}
return -1;
}
/* Pozitif yanit: beklenen SID + 0x40 olmali */
if (resp[0] != (unsigned char)(sid + UDS_POS_RESP_OFFSET)) {
fprintf(stderr, "Beklenmeyen yanit: 0x%02X (beklenen: 0x%02X)\n",
resp[0], sid + UDS_POS_RESP_OFFSET);
return -1;
}
return 0;
}
DiagnosticSessionControl — Oturum Açma
/* 0x10 DiagnosticSessionControl */
int uds_open_session(int sock, unsigned char session_type)
{
unsigned char req[2] = { 0x10, session_type };
unsigned char resp[64] = {0};
int n = uds_request(sock, req, sizeof(req), resp, sizeof(resp));
if (n < 0 || uds_check_response(resp, n, 0x10) < 0)
return -1;
/* resp[2..3] = P2_server (ms), resp[4..5] = P2*_server (10ms birimi) */
unsigned int p2 = (resp[2] << 8) | resp[3];
printf("Oturum acildi: tip=0x%02X P2=%u ms\n", session_type, p2);
return 0;
}
/* Kullanim:
// Extended session (0x03)
uds_open_session(sock, 0x03);
// Programming session (0x02)
uds_open_session(sock, 0x02);
*/
ReadDataByIdentifier — DID Okuma
/* 0x22 ReadDataByIdentifier */
int uds_read_did(int sock, unsigned short did,
unsigned char *out, int out_max)
{
unsigned char req[3] = {
0x22,
(unsigned char)(did >> 8),
(unsigned char)(did & 0xFF)
};
unsigned char resp[4096] = {0};
int n = uds_request(sock, req, sizeof(req), resp, sizeof(resp));
if (n < 0 || uds_check_response(resp, n, 0x22) < 0)
return -1;
/* resp[0]=0x62, resp[1..2]=DID echo, resp[3..]=veri */
int data_len = n - 3;
if (data_len <= 0 || data_len > out_max) return -1;
memcpy(out, resp + 3, data_len);
return data_len;
}
/* Kullanim: VIN okuma (DID 0xF190) */
unsigned char vin[18] = {0};
int len = uds_read_did(sock, 0xF190, vin, sizeof(vin) - 1);
if (len == 17) {
vin[17] = '\0';
printf("VIN: %s\n", vin);
}
SecurityAccess — Güvenlik Kilidi Açma
/* 0x27 SecurityAccess — seed/key akisi */
int uds_security_access(int sock, unsigned char level,
unsigned int secret_key)
{
unsigned char req[2], resp[64];
int n;
/* Adim 1: Seed talep et (accessType = level, tek sayı) */
req[0] = 0x27;
req[1] = level; /* orn: 0x01 */
n = uds_request(sock, req, 2, resp, sizeof(resp));
if (n < 0 || uds_check_response(resp, n, 0x27) < 0)
return -1;
/* resp[2..5] = 4-baytlık seed */
unsigned int seed = ((unsigned int)resp[2] << 24) |
((unsigned int)resp[3] << 16) |
((unsigned int)resp[4] << 8) |
(unsigned int)resp[5];
printf("Seed: 0x%08X\n", seed);
/* Adim 2: Key hesapla (basit XOR örnegi) */
unsigned int computed_key = seed ^ secret_key;
/* Adim 3: Key gönder (accessType = level + 1, cift sayı) */
unsigned char key_req[6] = {
0x27, (unsigned char)(level + 1),
(unsigned char)(computed_key >> 24),
(unsigned char)(computed_key >> 16),
(unsigned char)(computed_key >> 8),
(unsigned char)(computed_key & 0xFF)
};
n = uds_request(sock, key_req, sizeof(key_req), resp, sizeof(resp));
if (n < 0 || uds_check_response(resp, n, 0x27) < 0) {
fprintf(stderr, "SecurityAccess basarisiz!\n");
return -1;
}
printf("Guvenlik erisimi saglandi.\n");
return 0;
}
Tam UDS İstemci Kullanım Örneği
int main(void)
{
int sock = isotp_open("can0", 0x7DF, 0x7E8);
if (sock < 0) return 1;
/* Extended session ac */
if (uds_open_session(sock, 0x03) < 0) goto done;
/* VIN oku */
unsigned char vin[18] = {0};
int len = uds_read_did(sock, 0xF190, vin, 17);
if (len == 17) printf("VIN: %.17s\n", vin);
/* Guvenlik erisimi (yazma/kalibrasyon icin) */
if (uds_security_access(sock, 0x01, 0xDEADBEEF) == 0) {
/* Kalibrasyon parametresi yaz — ornek DID 0x1234 */
unsigned char cal_req[] = { 0x2E, 0x12, 0x34, 0x00, 0xFF };
unsigned char resp[16];
int n = uds_request(sock, cal_req, sizeof(cal_req),
resp, sizeof(resp));
uds_check_response(resp, n, 0x2E);
}
done:
close(sock);
return 0;
}
06 ECU Programlama Akışı
UDS üzerinden ECU firmware güncellemesi belirli bir servis sırasını gerektirir: oturum geçişi, güvenlik kilidi açma, indirme isteği, veri transferi ve transfer çıkışı.
ECU Programlama Adımları
1. DiagnosticSessionControl -> ProgrammingSession (0x10 02) 2. SecurityAccess -> Level 2 seed/key (0x27 03 / 0x27 04) 3. ControlDTCSetting -> off (0x85 02) 4. CommunicationControl -> disableRxAndTx (0x28 03) 5. RequestDownload -> adres + boyut (0x34) 6. TransferData -> firmware bloklari (0x36, tekrar) 7. RequestTransferExit -> butunluk dogrula (0x37) 8. RoutineControl -> CRC dogrulama (0x31 01 ROUTINE_ID) 9. ECUReset -> hard reset (0x11 01) 10. Yeni firmware aktif
RequestDownload (0x34)
/* 0x34 RequestDownload — indirilecek bellek bolgesi bildir */
int uds_request_download(int sock,
unsigned int mem_addr,
unsigned int mem_size,
unsigned int *max_block_out)
{
/* dataFormatIdentifier: compression=0, encryption=0 */
/* addressAndLengthFormatIdentifier: 4 bayt adres, 4 bayt uzunluk = 0x44 */
unsigned char req[10] = {
0x34,
0x00, /* dataFormatIdentifier */
0x44, /* addressAndLengthFormatIdentifier */
/* memoryAddress (4 bayt, big-endian) */
(unsigned char)(mem_addr >> 24),
(unsigned char)(mem_addr >> 16),
(unsigned char)(mem_addr >> 8),
(unsigned char)(mem_addr & 0xFF),
/* memorySize (4 bayt, big-endian) */
(unsigned char)(mem_size >> 24),
(unsigned char)(mem_size >> 16),
(unsigned char)(mem_size >> 8),
/* son bayt asagida */
};
/* 10. bayt: mem_size & 0xFF */
unsigned char req2[11];
memcpy(req2, req, 10);
req2[10] = (unsigned char)(mem_size & 0xFF);
unsigned char resp[16] = {0};
int n = uds_request(sock, req2, 11, resp, sizeof(resp));
if (n < 0 || uds_check_response(resp, n, 0x34) < 0)
return -1;
/* resp[1]: lengthFormatIdentifier — max blok uzunlugunun kac bayt oldugu */
int lfi = (resp[1] >> 4) & 0x0F;
unsigned int max_block = 0;
for (int i = 0; i < lfi; i++)
max_block = (max_block << 8) | resp[2 + i];
*max_block_out = max_block;
printf("Max blok boyutu: %u bayt\n", max_block);
return 0;
}
TransferData (0x36) — Blok Döngüsü
/* 0x36 TransferData — firmware bloklarini gonder */
int uds_flash_firmware(int sock, const char *fw_path,
unsigned int target_addr)
{
/* Firmware dosyasini ac */
FILE *f = fopen(fw_path, "rb");
if (!f) { perror("fopen"); return -1; }
fseek(f, 0, SEEK_END);
long fw_size = ftell(f);
fseek(f, 0, SEEK_SET);
unsigned char *fw = malloc(fw_size);
fread(fw, 1, fw_size, f);
fclose(f);
printf("Firmware boyutu: %ld bayt\n", fw_size);
/* RequestDownload */
unsigned int max_block = 0;
if (uds_request_download(sock, target_addr,
(unsigned int)fw_size,
&max_block) < 0) {
free(fw);
return -1;
}
max_block -= 2; /* SID + BSN overhead */
/* TransferData dongusu */
unsigned char req[4096 + 2];
unsigned char resp[16];
long offset = 0;
unsigned char bsc = 1; /* block sequence counter: 1..0xFF dongüsel */
while (offset < fw_size) {
long chunk = fw_size - offset;
if (chunk > max_block) chunk = max_block;
req[0] = 0x36;
req[1] = bsc;
memcpy(req + 2, fw + offset, chunk);
int n = uds_request(sock, req, (int)(chunk + 2),
resp, sizeof(resp));
if (n < 0 || uds_check_response(resp, n, 0x36) < 0) {
free(fw);
return -1;
}
offset += chunk;
bsc = (bsc == 0xFF) ? 0x01 : bsc + 1;
printf("\rTransfer: %ld/%ld bayt (%ld%%)",
offset, fw_size, offset * 100 / fw_size);
fflush(stdout);
}
printf("\nTransfer tamamlandi.\n");
/* RequestTransferExit (0x37) */
unsigned char exit_req[1] = { 0x37 };
int n = uds_request(sock, exit_req, 1, resp, sizeof(resp));
uds_check_response(resp, n, 0x37);
free(fw);
return 0;
}
ECU Sıfırlama ve Doğrulama
/* ECUReset sonrasi yeni firmware kontrol */
void uds_ecu_reset(int sock, unsigned char reset_type)
{
unsigned char req[2] = { 0x11, reset_type };
unsigned char resp[8] = {0};
uds_request(sock, req, 2, resp, sizeof(resp));
/* NRC 0x78 veya pozitif yanit beklenir; ECU sifirlanir */
printf("ECU sifirlanıyor (tip=0x%02X)...\n", reset_type);
}
/* 0x11 0x01 = hardReset */
uds_ecu_reset(sock, 0x01);
07 Python-CAN ve udsoncan Kütüphanesi
python-can + udsoncan kütüphane çifti, hızlı prototipleme, test otomasyonu ve testbench uygulamaları için eksiksiz bir Python UDS istemci yığını sağlar.
Kurulum
# Bağımlılıkları kur
pip3 install python-can can-isotp udsoncan
# Versiyon doğrulama
python3 -c "import udsoncan; print(udsoncan.__version__)"
# can0 arayüzünü hazırla
sudo modprobe can_isotp
sudo ip link set can0 type can bitrate 500000
sudo ip link set can0 up
Tam UDS İstemci Örneği
#!/usr/bin/env python3
"""
udsoncan ile UDS istemci ornegi
SocketCAN (can0) uzerinde ISO-TP ile ECU iletisimi
"""
import can
import isotp
import udsoncan
from udsoncan.connections import PythonIsoTpConnection
from udsoncan.services import DiagnosticSessionControl as DSC
from udsoncan.services import ReadDTCInformation
# --- Baglanti kurulumu ----------------------------------------------
bus = can.Bus(interface="socketcan", channel="can0", bitrate=500000)
addr = isotp.Address(
isotp.AddressingMode.Normal_11bits,
txid=0x7DF, # OBD-II broadcast tester ID
rxid=0x7E8, # ECU yanit ID
)
stack = isotp.CanStack(
bus=bus,
address=addr,
params=isotp.params.Params(blocksize=0, squash_stmin_requirement=False),
)
# --- UDS istemci konfigürasyonu -------------------------------------
config = udsoncan.configs.default_client_config.copy()
config["data_identifiers"] = {
0xF190: udsoncan.AsciiCodec(17), # VIN — 17 karakter ASCII
0xF194: udsoncan.AsciiCodec(10), # ECU donanim numarasi
0xF195: udsoncan.AsciiCodec(10), # ECU yazilim numarasi
}
# --- Kullanim --------------------------------------------------------
with PythonIsoTpConnection(stack) as conn:
with udsoncan.Client(conn, config=config) as client:
# 1) VIN oku (Default Session)
resp = client.read_data_by_identifier(0xF190)
vin = resp.service_data.values[0xF190]
print(f"VIN: {vin}")
# 2) Extended Session'a gec
client.change_session(DSC.Session.extendedDiagnosticSession)
# 3) Aktif DTC'leri oku
resp = client.get_dtc_by_status_mask(0xFF)
dtcs = resp.service_data.dtcs
if dtcs:
for dtc in dtcs:
print(f" DTC: {dtc.id:06X} Durum: {dtc.status.byte}")
else:
print("Aktif DTC yok.")
# 4) Default Session'a geri don
client.change_session(DSC.Session.defaultSession)
SecurityAccess ile Kalibrasyon Yazma
#!/usr/bin/env python3
import udsoncan
from udsoncan.connections import PythonIsoTpConnection
SECRET = 0xDEADBEEF
def compute_key(seed_bytes: bytes) -> bytes:
seed = int.from_bytes(seed_bytes, "big")
key = seed ^ SECRET
return key.to_bytes(4, "big")
with PythonIsoTpConnection(stack) as conn:
with udsoncan.Client(conn) as client:
# Extended session
client.change_session(
udsoncan.services.DiagnosticSessionControl.Session
.extendedDiagnosticSession)
# Seed talep et
resp = client.request_seed(access_type=0x01)
seed = resp.service_data.seed
print("Seed:", seed.hex().upper())
# Key hesapla ve gönder
key = compute_key(seed)
client.send_key(access_type=0x02, security_key=key)
print("Guvenlik erisimi saglandi!")
# Kalibrasyon DID yaz (ornek: 0x1234 = 0x00FF)
client.write_data_by_identifier(
0x1234,
udsoncan.RawCodec(2).decode(bytes([0x00, 0xFF]))
)
print("Kalibrasyon yazildi.")
TesterPresent ile Oturumu Canlı Tutma
import threading, time
def tester_present_loop(client, interval=2.0):
"""Her interval saniyede bir TesterPresent gonder"""
while getattr(tester_present_loop, "running", True):
try:
client.tester_present()
except Exception as exc:
print("TesterPresent hatasi:", exc)
break
time.sleep(interval)
# Arka planda calistir
tester_present_loop.running = True
tp = threading.Thread(target=tester_present_loop, args=(client,), daemon=True)
tp.start()
# ... uzun suren islemler ...
tester_present_loop.running = False
08 Hata Ayıklama Araçları
CAN trafik izleme araçları, ISO-TP dump yardımcıları ve Wireshark CAN dissector ile UDS iletişim sorunlarını tespit etmek mümkündür.
candump ile Trafik İzleme
# Tüm CAN çerçevelerini izle
candump can0
# ID filtreli izleme — sadece 0x7DF ve 0x7E8
candump can0 7DF:7FF 7E8:7FF
# Zaman damgalı ve ASCII dump
candump -t a -a can0
# Log dosyasına kaydet
candump -L can0 > /tmp/can_session.log
# Log dosyasını yeniden oynat
canplayer -I /tmp/can_session.log can0
isotpsend / isotprecv ile Hızlı Test
# isotpsend: tek payload gonder (ISO-TP cercevelemesi otomatik)
isotpsend -s 7DF -d 7E8 can0 <<< "22 F1 90"
# isotprecv: yanit bekle (arka planda calıstır)
isotprecv -s 7E8 -d 7DF can0 &
# isotpdump: ISO-TP trafigi birlestirilerek goster
isotpdump -s 7DF -d 7E8 can0
# Ornek isotpdump ciktisi:
# 0.000123 can0 TX [3] 22 F1 90
# 0.003456 can0 RX [20] 62 F1 90 57 41 55 5A 5A 5A ...
# Zaman damgali cikti
isotpdump -t -s 7DF -d 7E8 can0
Wireshark ile Analiz
# candump log'unu pcap'e donustur
candump -L can0 > can_session.log
# cantools ile pcap'e cevir
python3 -m cantools convert can_session.log capture.pcap
# Wireshark CLI ile ISO-TP dissector
tshark -r capture.pcap \
-Y "can.id == 0x7df || can.id == 0x7e8" \
-T fields \
-e frame.time_relative \
-e can.id \
-e can.data
udsoncan ile Debug Loglaması
import logging
# udsoncan ve isotp debug loglarini etkinlestir
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("udsoncan").setLevel(logging.DEBUG)
logging.getLogger("isotp").setLevel(logging.DEBUG)
# Ornek cikti:
# DEBUG udsoncan: Sending 0x10 0x03
# DEBUG isotp: Sending SF id=0x7DF data=10 03
# DEBUG isotp: Received SF id=0x7E8 data=50 03 00 19 01 F4
# DEBUG udsoncan: Received positive response 0x50 0x03
Sık Karşılaşılan Sorunlar ve Çözümleri
| Sorun | Semptom | Çözüm |
|---|---|---|
| Yanıt yok | TX görünüyor, RX yok | RX ID'yi kontrol et; ECU aktif mi? Oturum modu doğru mu? |
| NRC 0x22 | conditionsNotCorrect | Önce ExtendedSession veya ProgrammingSession'a geç |
| NRC 0x33 | securityAccessDenied | 0x27 RequestSeed → key hesapla → SendKey akışını tamamla |
| NRC 0x35 | invalidKey | Seed/key algoritması hatalı; ECU dokümanını kontrol et |
| NRC 0x36 | exceededNumberOfAttempts | ECU geçici kilitli; OEM belgelenen bekleme süresini uygula |
| NRC 0x78 tekrar gelmiyor | ResponsePending sona erdi | Poling süresini artır; TesterPresent göndermeye devam et |
| ISO-TP Flow Control gelmiyor | FF gönderildi, FC yok | can_isotp modülü yüklü mü? Soket bind adresleri doğru mu? |
| TransferData sekans hatası | NRC 0x73 | block_sequence_counter 0x01'den başlamalı; 0xFF sonrası 0x01'e döner |
| Padding hatası | Bind başarısız veya garip yanıtlar | ECU TX_PADDING bekliyorsa CAN_ISOTP_TX_PADDING bayrağını set et |