Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX SOCKETCAN 2026

SocketCAN
Linux CAN Bus Arayüzü

AF_CAN socket'ten python-can'a — Linux'ta CAN bus programlamanın bütünü.

00 CAN bus ve SocketCAN

CAN (Controller Area Network), Bosch tarafından 1980'lerde geliştirilen, otomotiv, endüstriyel otomasyon ve tıbbi cihazlarda yaygın olarak kullanılan dayanıklı bir seri haberleşme protokolüdür.

CAN, bir otomobilin farklı ECU'larının (Engine Control Unit, ABS, airbag, klima) birbirleriyle güvenilir biçimde haberleşmesini sağlamak amacıyla tasarlandı. O günden bu yana uygulama alanı fabrika otomasyonuna, robot sistemlerine, tıbbi cihazlara ve havacılık uygulamalarına kadar genişledi.

CAN özellikleri

Multi-masterBus üzerindeki her node mesaj gönderebilir; merkezi bir master yoktur. Arbitration mekanizması çakışmaları çözer.
CSMA/CD+AMPCarrier Sense Multiple Access / Collision Detection + Arbitration on Message Priority. Düşük ID'li mesaj öncelik kazanır; yüksek ID'li node gönderimi bırakır.
Differential signalingCAN_H ve CAN_L çift tel üzerinden diferansiyel sinyalleme. Gürültüye karşı son derece dayanıklı; otomotiv EMI ortamlarında kritik.
HızKlasik CAN: maksimum 1 Mbit/s. CAN FD (Flexible Data Rate): veri fazında 8 Mbit/s ve üzerine çıkabilir.
Hata tespitiCRC, bit stuffing, frame check, ACK kontrolü ile çok katmanlı hata tespiti. Hata sayacına göre node Error Active → Error Passive → Bus-Off durumuna geçer.

CAN frame türleri

Frame türüAçıklamaKullanım
Data frame0-8 byte veri taşır; en yaygın kullanılan türSensör verisi, komut gönderimi
Remote frame (RTR)Veri içermez; belirli ID'den veri isterİstek-cevap protokolleri (nadiren)
Error frameHata tespitinde node tarafından otomatik üretilirBus hata bildirimi
Overload frameAlıcı node yavaşlama talep ederReceiver kalabalık; modern devrelerde nadiren

SocketCAN: Linux'ta CAN ağ arayüzü

SocketCAN, CAN donanımını Linux ağ yığınına entegre eden bir kernel altyapısıdır. Temel yenilik, CAN'ı bir ağ arayüzü (network interface) olarak modellemesidir: can0, can1 gibi arayüzler eth0 gibi davranır.

Kullanıcı alanı (uygulama)
    ↕ BSD socket API: socket() / bind() / read() / write()
AF_CAN / PF_CAN socket layer
    ↕
CAN network device layer (can_dev)
    ↕
SoC/USB CAN controller driver (mcp251x, flexcan, gs_usb ...)
    ↕
CAN transceiver (TJA1050, SN65HVD230 ...)
    ↕
CAN bus (diferansiyel kablo)

SocketCAN öncesinde her donanım üreticisinin kendi özel API'si vardı: bir karttan diğerine geçiş, uygulama kodunun baştan yazılmasını gerektiriyordu. SocketCAN bu parçalanmışlığı ortadan kaldırır.

NOT

SocketCAN, CAN uygulama protokollerini (CANopen, SAE J1939, AUTOSAR COM) kapsamamaktadır. Bu protokoller kullanıcı alanında ayrı kütüphaneler (lely-core, libopencan, canlib) tarafından implemente edilir ve SocketCAN'ı transport katmanı olarak kullanır.

01 CAN frame yapısı

Linux'ta CAN frame'leri struct can_frame ve CAN FD için struct canfd_frame yapılarıyla temsil edilir; ID alanındaki flag bitleri frame türünü belirler.

Standard vs Extended frame

FormatID uzunluğuID aralığıFlag
Standard Frame Format (SFF)11 bit0x000 – 0x7FF
Extended Frame Format (EFF)29 bit0x00000000 – 0x1FFFFFFFCAN_EFF_FLAG

struct can_frame

include/uapi/linux/can.h
typedef __u32 canid_t;

/* Flag bitleri — can_id'nin üst bitleri */
#define CAN_EFF_FLAG  0x80000000U  /* 29-bit extended ID */
#define CAN_RTR_FLAG  0x40000000U  /* Remote Transmission Request */
#define CAN_ERR_FLAG  0x20000000U  /* Error frame */
#define CAN_SFF_MASK  0x000007FFU  /* 11-bit ID maskesi */
#define CAN_EFF_MASK  0x1FFFFFFFU  /* 29-bit ID maskesi */

struct can_frame {
    canid_t  can_id;     /* ID + EFF/RTR/ERR flag'ları */
    __u8     can_dlc;    /* Data Length Code: 0-8 byte */
    __u8     __pad;
    __u8     __res0;
    __u8     __res1;
    __u8     data[8] __attribute__((aligned(8)));
};

/* CAN FD frame — 64 byte payload, hız anahtarı */
struct canfd_frame {
    canid_t  can_id;
    __u8     len;         /* 0-64 byte */
    __u8     flags;       /* CANFD_BRS: bit rate switch, CANFD_ESI */
    __u8     __res0;
    __u8     __res1;
    __u8     data[64] __attribute__((aligned(8)));
};

CAN ID ve flag kullanımı

can_id_examples.c
#include <linux/can.h>

/* Standard frame, ID = 0x123 */
canid_t id_std = 0x123;

/* Extended frame, ID = 0x12345678 */
canid_t id_ext = 0x12345678 | CAN_EFF_FLAG;

/* Standard Remote frame, ID = 0x123 */
canid_t id_rtr = 0x123 | CAN_RTR_FLAG;

/* ID'yi çıkarmak için mask uygula */
uint32_t raw_id = id_ext & CAN_EFF_MASK;   /* 0x12345678 */

/* Extended mi kontrol et */
if (frame.can_id & CAN_EFF_FLAG)
    printf("Extended frame: 0x%08X\n", frame.can_id & CAN_EFF_MASK);
else
    printf("Standard frame: 0x%03X\n", frame.can_id & CAN_SFF_MASK);

/* Hata frame'i mi? */
if (frame.can_id & CAN_ERR_FLAG)
    printf("Error frame, error class: 0x%08X\n",
           frame.can_id & CAN_ERR_MASK);

Arbitration — öncelik mekanizması

CAN bus'ta iki node aynı anda göndermeye başlarsa arbitration devreye girer. Her node kendi gönderdiği biti okur; dominant bit (0) recessive bit'i (1) bastırır. Daha düşük ID değerine sahip node (daha fazla dominant bit) arbitration'ı kazanır ve göndermeye devam eder. Kaybeden node gönderimi durdurup alıcı konumuna geçer.

Node A: ID = 0x100  → 0 0 0 1 0 0 0 0 0 0 0
Node B: ID = 0x200  → 0 1 0 0 0 0 0 0 0 0 0
                          ↑
                    Bit 9'da çakışma:
                    A=0 (dominant), B=1 (recessive)
                    → A kazanır, B alıcı olur

02 Kernel modülleri ve donanım

SocketCAN, katmanlı bir kernel modül mimarisi üzerine inşa edilmiştir; core modüller tüm CAN uygulamaları için ortak altyapıyı sağlar.

Temel modüller

canCAN protokol ailesi çekirdeği. AF_CAN socket desteği. Diğer tüm CAN modüllerinin bağımlılığı.
can_rawSOCK_RAW CAN soketleri. Filtreleme, error frame alma ve CAN FD desteği.
can_bcmBroadcast Manager: periyodik frame gönderimi ve Rx multiplexing.
can_isotpISO 15765-2 transport protokolü. Kernel 5.10+'da birleşik. OBD-II ve UDS için.
can_j1939SAE J1939 protokol stack. Kernel 5.4+'da birleşik. Ağır vasıta araçlar için.
can_devCAN network device framework. Tüm hardware driver'larının bağımlılığı; istatistik, netlink, biterr desteği.

SPI bağlantılı CAN: mcp251x

MCP2515, yaygın kullanılan SPI bağlantılı CAN controller'dır. Raspberry Pi ve benzeri SBC'lerde sıkça kullanılır. Device Tree binding'i aşağıdaki gibidir:

overlay.dts — MCP2515 DT binding
&spi0 {
    mcp2515: can@0 {
        compatible = "microchip,mcp2515";
        reg = <0>;          /* SPI CS0 */
        spi-max-frequency = <10000000>;  /* 10 MHz */
        clocks = <&mcp2515_clk>;
        interrupt-parent = <&gpio>;
        interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
    };
};

Yerleşik CAN controller'lar

Driver modülüDonanımKullanılan platform
flexcanFlexCAN IP bloğuNXP i.MX6, i.MX8, LS1021A
c_can_platformC_CAN/D_CAN IP bloğuTI AM335x (BeagleBone), AM437x
sun4i_canAllwinner CANAllwinner A10, A20 SoC
rcar_canfdRenesas CAN FDRenesas R-Car H3, M3
stm32_fdcanSTM32 FDCANSTM32H7, STM32G0

USB CAN adaptörler

DriverDonanımNotlar
gs_usbCanable, Candlelight, GVRETAçık kaynak firmware, uygun fiyatlı
kvaser_usbKvaser Leaf, USBcanEndüstriyel sınıf
peak_usbPEAK PCAN-USBYaygın, geniş yazılım desteği
ems_usbEMS CPC-USBISO 15765-4 donanım desteği

Virtual CAN: vcan

Gerçek donanım olmadan CAN uygulamalarını test etmek için vcan modülü kullanılır. vcan arayüzü, gönderilen frame'i hemen aynı socket'e geri yansıtır; loopback gibi çalışır.

terminal — vcan kurulumu
# Modülleri yükle
sudo modprobe can
sudo modprobe can_raw
sudo modprobe vcan

# Virtual arayüz oluştur ve aç
sudo ip link add dev vcan0 type vcan
sudo ip link set vcan0 up

# Arayüzü doğrula
ip link show vcan0
# 4: vcan0: <NOARP,UP,LOWER_UP> mtu 72 qdisc noqueue state UNKNOWN

03 ip link ile CAN yapılandırması

CAN arayüzü bitrate, sample-point ve diğer parametreler ip link komutu ile yapılandırılır; arayüz açılmadan önce tüm parametrelerin ayarlanmış olması gerekir.

Temel CAN arayüzü açma

terminal — CAN arayüzü yapılandırma
# Arayüzü kapat (yapılandırma yapmadan önce)
sudo ip link set can0 down

# 500 kbit/s bitrate ile aç
sudo ip link set can0 up type can bitrate 500000

# Bitrate + sample-point (0.875 = %87.5) birlikte
sudo ip link set can0 up type can bitrate 500000 sample-point 0.875

# Triple sampling ile (gürültülü ortamlar)
sudo ip link set can0 up type can bitrate 250000 triple-sampling on

CAN FD yapılandırması

terminal — CAN FD arayüzü
# CAN FD: nominal 500 kbit/s, data fazı 2 Mbit/s
sudo ip link set can0 up type can \
    bitrate 500000 \
    dbitrate 2000000 \
    fd on

# CAN FD + sample-point her iki faz için
sudo ip link set can0 up type can \
    bitrate 500000 sample-point 0.875 \
    dbitrate 2000000 dsample-point 0.75 \
    fd on

Arayüz bilgisi ve istatistik

terminal — arayüz durum sorgusu
# Detaylı arayüz bilgisi — bitrate, sample-point, durum
ip -details link show can0

# Örnek çıktı:
# 3: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP
#     link/can
#     can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
#     bitrate 500000 sample-point 0.875
#     tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
#     mcp251x: tseg1 3..16 tseg2 2..8 sjw 1..4 brp 1..64 brp-inc 1

# TX/RX/error sayaçları
ip -s link show can0

Bus-off recovery

TX hata sayacı 255'i geçtiğinde CAN controller Bus-Off durumuna girer ve bus'tan ayrılır. Otomatik recovery için restart-ms parametresi kullanılır:

terminal — bus-off yapılandırması
# 100ms sonra otomatik recovery dene
sudo ip link set can0 up type can bitrate 500000 restart-ms 100

# Manuel recovery (otomatik yoksa)
sudo ip link set can0 type can restart

# Durumu kontrol et
ip -details link show can0 | grep "can state"
# can state ERROR-ACTIVE  ← normal çalışma
# can state BUS-OFF        ← hata durumu

Loopback ve listen-only modları

terminal — özel modlar
# Loopback: gönderilen frame'ler kendi socket'lerinde de görünür
sudo ip link set can0 up type can bitrate 500000 loopback on

# Listen-only: hiçbir şey göndermez, ACK bile vermez — pasif izleme
sudo ip link set can0 up type can bitrate 500000 listen-only on
UYARI

listen-only modunda CAN controller ACK biti göndermediğinden, bus'taki diğer node'lar gönderilen mesajlara ACK alamaz ve ACK error üretir. Bu mod yalnızca başka bir dinleyicinin olduğu test ortamlarında veya pasif loglama senaryolarında kullanılmalıdır.

04 Raw socket ile gönderme ve alma

CAN frame'leri gönderip almak için standart POSIX socket API kullanılır; AF_CAN adres ailesi ve CAN_RAW protokolü ile düşük seviyeli erişim sağlanır.

Tam gönderici örneği

can_send.c — CAN frame gönderme
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>

int main(void)
{
    int sock;
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_frame frame;
    int nbytes;

    /* 1. Raw CAN socket aç */
    sock = socket(AF_CAN, SOCK_RAW, CAN_RAW);
    if (sock < 0) {
        perror("socket");
        return 1;
    }

    /* 2. Arayüz index'ini al */
    strncpy(ifr.ifr_name, "can0", IFNAMSIZ - 1);
    if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {
        perror("ioctl SIOCGIFINDEX");
        close(sock);
        return 1;
    }

    /* 3. Socket'i can0 arayüzüne bağla */
    memset(&addr, 0, sizeof(addr));
    addr.can_family  = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;

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

    /* 4. Frame hazırla: ID=0x123, 8 byte veri */
    memset(&frame, 0, sizeof(frame));
    frame.can_id  = 0x123;
    frame.can_dlc = 8;
    frame.data[0] = 0xDE;
    frame.data[1] = 0xAD;
    frame.data[2] = 0xBE;
    frame.data[3] = 0xEF;
    frame.data[4] = 0x01;
    frame.data[5] = 0x02;
    frame.data[6] = 0x03;
    frame.data[7] = 0x04;

    /* 5. Frame gönder */
    nbytes = write(sock, &frame, sizeof(frame));
    if (nbytes != sizeof(frame)) {
        perror("write");
        close(sock);
        return 1;
    }

    printf("Frame gönderildi: can_id=0x%03X, dlc=%d\n",
           frame.can_id, frame.can_dlc);

    close(sock);
    return 0;
}

Alıcı ve select() ile non-blocking

can_recv.c — CAN frame alma
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <linux/can.h>
#include <linux/can/raw.h>

int main(void)
{
    int sock, nbytes;
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_frame frame;
    fd_set rdfs;
    struct timeval tv;

    sock = socket(AF_CAN, SOCK_RAW, CAN_RAW);
    if (sock < 0) { perror("socket"); return 1; }

    strncpy(ifr.ifr_name, "can0", IFNAMSIZ - 1);
    ioctl(sock, SIOCGIFINDEX, &ifr);

    memset(&addr, 0, sizeof(addr));
    addr.can_family  = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    bind(sock, (struct sockaddr *)&addr, sizeof(addr));

    printf("can0 dinleniyor...\n");

    while (1) {
        /* select() ile 1 saniyelik timeout */
        FD_ZERO(&rdfs);
        FD_SET(sock, &rdfs);
        tv.tv_sec  = 1;
        tv.tv_usec = 0;

        int ret = select(sock + 1, &rdfs, NULL, NULL, &tv);
        if (ret < 0) { perror("select"); break; }
        if (ret == 0) { printf("timeout — frame yok\n"); continue; }

        nbytes = read(sock, &frame, sizeof(frame));
        if (nbytes < 0) { perror("read"); break; }
        if (nbytes < (int)sizeof(struct can_frame)) {
            fprintf(stderr, "eksik frame\n"); continue;
        }

        printf("[0x%03X] dlc=%d data:", frame.can_id & CAN_SFF_MASK,
               frame.can_dlc);
        for (int i = 0; i < frame.can_dlc; i++)
            printf(" %02X", frame.data[i]);
        printf("\n");
    }

    close(sock);
    return 0;
}
terminal — derleme ve çalıştırma
gcc -Wall -o can_send can_send.c
gcc -Wall -o can_recv can_recv.c

# Bir terminalde alıcıyı başlat
./can_recv

# Başka terminalde gönder
./can_send

05 Filtreleme

Raw socket'e filtre listesi atanarak yalnızca belirli ID veya ID aralıklarından gelen frame'ler uygulamaya iletilir; kernel katmanında yapılan bu filtreleme CPU yükünü azaltır.

struct can_filter ve setsockopt

include/uapi/linux/can/raw.h
struct can_filter {
    canid_t  can_id;    /* aranan değer */
    canid_t  can_mask;  /* hangi bitler kontrol edilecek */
};
/* Eşleşme koşulu: (frame.can_id & mask) == (can_id & mask) */
can_filter.c — filtre listesi kurma
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>

int main(void)
{
    int sock;
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_frame frame;

    sock = socket(AF_CAN, SOCK_RAW, CAN_RAW);
    strncpy(ifr.ifr_name, "can0", IFNAMSIZ - 1);
    ioctl(sock, SIOCGIFINDEX, &ifr);
    memset(&addr, 0, sizeof(addr));
    addr.can_family  = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    bind(sock, (struct sockaddr *)&addr, sizeof(addr));

    /* Filtre 1: tam eşleşme — yalnızca ID=0x123 */
    /* Filtre 2: aralık  — 0x200-0x2FF arası  */
    struct can_filter filters[2] = {
        { .can_id = 0x123, .can_mask = CAN_SFF_MASK },
        { .can_id = 0x200, .can_mask = 0x700        },
    };

    setsockopt(sock, SOL_CAN_RAW, CAN_RAW_FILTER,
               filters, sizeof(filters));

    /* Error frame'leri de al */
    can_err_mask_t err_mask = CAN_ERR_TX_TIMEOUT | CAN_ERR_BUSOFF
                            | CAN_ERR_CRTL;
    setsockopt(sock, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
               &err_mask, sizeof(err_mask));

    while (1) {
        int nbytes = read(sock, &frame, sizeof(frame));
        if (nbytes < 0) { perror("read"); break; }

        if (frame.can_id & CAN_ERR_FLAG) {
            printf("HATA frame: 0x%08X\n", frame.can_id);
            continue;
        }
        printf("[0x%03X] dlc=%d\n", frame.can_id, frame.can_dlc);
    }

    close(sock);
    return 0;
}

CAN FD frame etkinleştirme

can_fd_recv.c — FD frame desteği
#include <linux/can.h>
#include <linux/can/raw.h>

/* CAN FD frame'leri etkinleştir */
int enable_fd = 1;
setsockopt(sock, SOL_CAN_RAW, CAN_RAW_FD_FRAMES,
           &enable_fd, sizeof(enable_fd));

/* FD socket'ten okuma */
struct canfd_frame fdframe;
int nbytes = read(sock, &fdframe, sizeof(fdframe));
if (nbytes == sizeof(struct can_frame)) {
    /* Klasik CAN frame */
} else if (nbytes == sizeof(struct canfd_frame)) {
    /* CAN FD frame — fdframe.len: 0-64 */
    printf("FD frame: ID=0x%X len=%d flags=0x%02X\n",
           fdframe.can_id, fdframe.len, fdframe.flags);
}
NOT

Filtre maskesi, hangi bitlerin karşılaştırılacağını belirler. can_mask = 0x7FF 11-bit ID'nin tüm bitlerini karşılaştırır (tam eşleşme). can_mask = 0x700 yalnızca üst 3 biti karşılaştırır; bu sayede bir 256 ID bloğu tek filtre ile yakalanabilir. can_mask = 0x000 ise tüm frame'leri geçirir.

06 BCM — Broadcast Manager

BCM (Broadcast Manager), periyodik CAN frame gönderimi ve Rx multiplexing için kernel içinde bir zamanlayıcı altyapısı sunar; SOCK_DGRAM ve özel mesaj yapısı ile kullanılır.

struct bcm_msg_head

include/uapi/linux/can/bcm.h (sadeleştirilmiş)
struct bcm_msg_head {
    __u32          opcode;       /* TX_SETUP, TX_DELETE, RX_SETUP, RX_READ ... */
    __u32          flags;        /* SETTIMER, STARTTIMER, TX_COUNTEVT ... */
    __u32          count;        /* kaç kez gönderilecek (0=sonsuz) */
    struct timeval ival1;        /* count adet gönderim aralığı */
    struct timeval ival2;        /* sonraki periyodik gönderim aralığı */
    canid_t        can_id;      /* mesaj ID */
    __u32          nframes;      /* kaç frame var */
    struct can_frame frames[0]; /* esnek dizi — nframes adet frame */
};

TX_SETUP: periyodik gönderim

bcm_tx.c — 100ms periyodik gönderim
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/bcm.h>

struct bcm_tx_msg {
    struct bcm_msg_head head;
    struct can_frame    frame;
};

int main(void)
{
    int sock;
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct bcm_tx_msg msg;

    /* BCM için SOCK_DGRAM kullanılır */
    sock = socket(AF_CAN, SOCK_DGRAM, CAN_BCM);
    if (sock < 0) { perror("socket"); return 1; }

    strncpy(ifr.ifr_name, "can0", IFNAMSIZ - 1);
    ioctl(sock, SIOCGIFINDEX, &ifr);

    memset(&addr, 0, sizeof(addr));
    addr.can_family  = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;

    /* BCM: connect() kullanılır, bind() değil */
    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("connect"); close(sock); return 1;
    }

    memset(&msg, 0, sizeof(msg));

    msg.head.opcode        = TX_SETUP;
    msg.head.flags         = SETTIMER | STARTTIMER;
    msg.head.count         = 0;
    msg.head.ival1.tv_sec  = 0;
    msg.head.ival1.tv_usec = 0;
    msg.head.ival2.tv_sec  = 0;
    msg.head.ival2.tv_usec = 100000;  /* 100ms */
    msg.head.can_id        = 0x701;
    msg.head.nframes       = 1;

    msg.frame.can_id  = 0x701;
    msg.frame.can_dlc = 1;
    msg.frame.data[0] = 0x05;  /* CANopen NMT: operational */

    if (write(sock, &msg, sizeof(msg)) < 0) {
        perror("write"); close(sock); return 1;
    }

    printf("0x701 heartbeat başlatıldı — 100ms periyot\n");
    pause();

    close(sock);
    return 0;
}

RX_SETUP: değişim yakalama ve timeout tespiti

bcm_rx.c — mesaj değişimi ve timeout izleme
struct bcm_rx_msg {
    struct bcm_msg_head head;
    struct can_frame    frame;
};

/* RX_SETUP: 0x123 ID'li mesajı izle
   - içerik değişince bildir (RX_CHANGED)
   - 500ms gelmezse timeout bildir (RX_TIMEOUT) */
struct bcm_rx_msg rxmsg;
memset(&rxmsg, 0, sizeof(rxmsg));

rxmsg.head.opcode        = RX_SETUP;
rxmsg.head.flags         = SETTIMER | RX_FILTER_ID;
rxmsg.head.ival1.tv_sec  = 0;
rxmsg.head.ival1.tv_usec = 0;
rxmsg.head.ival2.tv_sec  = 0;
rxmsg.head.ival2.tv_usec = 500000;  /* 500ms timeout */
rxmsg.head.can_id        = 0x123;
rxmsg.head.nframes       = 1;

rxmsg.frame.can_id  = 0x123;
rxmsg.frame.can_dlc = 8;
memset(rxmsg.frame.data, 0xFF, 8);

write(sock, &rxmsg, sizeof(rxmsg));

struct bcm_rx_msg event;
while (1) {
    int n = read(sock, &event, sizeof(event));
    if (n < 0) { perror("read"); break; }

    switch (event.head.opcode) {
    case RX_CHANGED:
        printf("0x123 değişti: %02X %02X...\n",
               event.frame.data[0], event.frame.data[1]);
        break;
    case RX_TIMEOUT:
        printf("0x123 timeout — cihaz cevap vermiyor!\n");
        break;
    }
}

07 candump ve cansend araçları

can-utils paketi, CAN bus'unu izlemek, test etmek ve kayıt altına almak için kullanılan temel komut satırı araçlarını içerir.

Kurulum

terminal — can-utils kurulumu
# Debian/Ubuntu/Raspberry Pi OS
sudo apt-get install can-utils

# Yocto Project — local.conf veya recipe'ye ekle
IMAGE_INSTALL:append = " can-utils"

# Buildroot
make menuconfig
# Target packages → Networking applications → can-utils

candump

terminal — candump kullanımı
# Tüm trafiği ekrana yaz
candump can0

# Örnek çıktı:
#  (1703074800.123456)  can0  123#DEADBEEF01020304
#  (1703074800.234567)  can0  456#0102030405060708

# Log dosyasına yaz
candump -l can0
# Oluşan dosya: candump-2026-04-12_134523.log

# Belirli ID filtresiyle — sadece 0x123 ve 0x456
candump can0 123:7FF 456:7FF

# Kaç frame alındığında dur
candump -n 100 can0

# Çoklu arayüz aynı anda
candump can0 can1 vcan0

canplayer — log tekrar oynatma

terminal — canplayer
# candump log'unu tekrar oynat
canplayer -I candump-2026-04-12_134523.log

# Farklı interface'e oynat (can0 yerine vcan0)
canplayer -I candump.log vcan0=can0

cansend ve cangen

terminal — cansend ve cangen
# Tek frame gönder: ID#DATA (hex)
cansend can0 123#DEADBEEF

# Extended ID (8 hex karakter)
cansend can0 12345678#01020304AABBCCDD

# Remote frame (RTR)
cansend can0 123#R

# CAN FD frame (## ayırıcı, flags byte)
cansend can0 123##100112233445566778899AABBCCDDEEFF

# cangen: yük testi için rastgele frame
cangen can0

# Belirli parametrelerle
cangen -g 5 -I 0x100 -L 8 can0
# -g: gecikme (ms), -I: base ID, -L: DLC

Diğer can-utils araçları

canbusloadBus yük yüzdesini hesaplar. Örnek: canbusload can0@500000 — 500 kbit/s'de yük oranı.
canfdtestCAN FD bağlantısını test eder; farklı payload boyutlarıyla round-trip testi yapar.
isotpdumpISO 15765-2 (ISO-TP) çok parçalı mesajları decode eder. OBD-II ve UDS trafik analizi.
isotpsend / isotprecvISO-TP katmanı üzerinden uzun mesaj gönderme ve alma. Kernel CAN_ISOTP modülü gerekli.
j1939acdSAE J1939 address claiming daemon. J1939 SA (source address) çakışmalarını çözer.
terminal — canbusload örneği
# can0 üzerinde 500 kbit/s için bus yükünü 1s aralıkla ölç
canbusload can0@500000 -r 1 -e -b

# Örnek çıktı:
# can0@500000   57 frames/s   13.680 kbit/s   2.7% bus load

08 Python ile SocketCAN

python-can kütüphanesi, SocketCAN dahil pek çok CAN arayüzünü soyutlayan Pythonic bir API sunar; hızlı prototip ve test için idealdir.

Kurulum ve temel kullanım

terminal — python-can kurulumu
# pip ile kur
pip install python-can

# Geliştirme ortamı için virtual env
python3 -m venv venv
source venv/bin/activate
pip install python-can
can_basic.py — gönderme ve alma
import can
import time

# can0 arayüzüne bağlan
bus = can.interface.Bus(channel='can0', bustype='socketcan')

# Frame gönder
msg = can.Message(
    arbitration_id=0x123,
    data=[0xDE, 0xAD, 0xBE, 0xEF],
    is_extended_id=False
)
bus.send(msg)
print(f"Gönderildi: {msg}")

# Tek frame al — 1 saniye timeout
received = bus.recv(timeout=1.0)
if received:
    print(f"Alındı: ID=0x{received.arbitration_id:03X} "
          f"data={received.data.hex()}")
else:
    print("Timeout — frame gelmedi")

bus.shutdown()

can.Message yapısı

arbitration_idCAN ID (integer). Standard 11-bit veya extended 29-bit.
is_extended_idTrue ise 29-bit extended ID kullanılır. False (varsayılan) ise 11-bit.
databytes, bytearray veya int listesi. CAN için 0-8, CAN FD için 0-64 byte.
is_fdTrue ise CAN FD frame olarak işaretlenir.
is_remote_frameTrue ise RTR (Remote Transmission Request) frame.
timestampAlınan frame'in kernel timestamp'i (float, saniye cinsinden).

OBD-II PID sorgusu

obd2_query.py — motor devri (RPM) sorgusu
import can

bus = can.interface.Bus(channel='can0', bustype='socketcan')

# OBD-II sorgu frame'i:
# 0x7DF = broadcast functional address
# [02]  = 2 byte veri uzunluğu
# [01]  = Mode 01: current data
# [0C]  = PID 0x0C: engine RPM
query = can.Message(
    arbitration_id=0x7DF,
    data=[0x02, 0x01, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00],
    is_extended_id=False
)
bus.send(query)

# Cevap 0x7E8 adresinden beklenir
while True:
    resp = bus.recv(timeout=0.5)
    if resp is None:
        print("Cevap yok")
        break
    if resp.arbitration_id == 0x7E8:
        if resp.data[1] == 0x41 and resp.data[2] == 0x0C:
            rpm = ((resp.data[3] * 256) + resp.data[4]) / 4
            print(f"Motor Devri: {rpm:.0f} RPM")
            break

bus.shutdown()

Asenkron alım: Notifier ve BufferedReader

can_async.py — BufferedReader ile asenkron alım
import can
import time

bus = can.interface.Bus(channel='can0', bustype='socketcan')

reader   = can.BufferedReader()
notifier = can.Notifier(bus, [reader])

print("5 saniye CAN trafiği yakalanıyor...")
time.sleep(5)
notifier.stop()

count = 0
while True:
    msg = reader.get_message(timeout=0)
    if msg is None:
        break
    print(f"[{msg.timestamp:.6f}] 0x{msg.arbitration_id:03X}: {msg.data.hex()}")
    count += 1

print(f"Toplam {count} frame yakalandı")
bus.shutdown()

CSV Logger

can_logger.py — dosyaya kayıt
import can

bus    = can.interface.Bus(channel='can0', bustype='socketcan')
logger = can.Logger('can_log.csv')

notifier = can.Notifier(bus, [logger])

try:
    import time
    time.sleep(60)
finally:
    notifier.stop()
    logger.stop()
    bus.shutdown()
    print("Kayıt tamamlandı: can_log.csv")

09 Ağ istatistikleri ve hata yönetimi

CAN bus hataları katmanlı bir mekanizma ile yönetilir; istatistikler kernel tarafından tutulur ve SocketCAN üzerinden izlenebilir.

ip -s link show ile istatistikler

terminal — istatistik sorgusu
ip -s link show can0

# Örnek çıktı:
# 3: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP
#     link/can
#     RX:  bytes  packets  errors  dropped missed  mcast
#          12345   1543      0       0       0       0
#     TX:  bytes  packets  errors  dropped carrier  collsns
#           9876   1234      0       0       0         0

# Kernel CAN protokol istatistikleri
cat /proc/net/can/stats

# Per-socket istatistikleri
cat /proc/net/can/rcvlist_all

CAN hata durumu makinesi

Her CAN controller, TX Error Counter (TEC) ve RX Error Counter (REC) tutarak hata durumunu yönetir. Bu sayaçlar iletişim kalitesini gösterir ve node'un bus'a katılım yetkisini belirler.

Error Active (TEC < 128, REC < 128)
    ↓ TEC ≥ 128 ya da REC ≥ 128
Error Passive (hata flag gönderiminde farklı format)
    ↓ TEC ≥ 256
Bus-Off (bus'tan tamamen ayrılır)
    ↓ 128 × 11 recessive bit görünce
Error Active (kurtarıldı)

Error frame türleri ve decode

can_error.c — error frame decode
#include <linux/can/error.h>

void decode_error_frame(const struct can_frame *frame)
{
    canid_t eid = frame->can_id & CAN_ERR_MASK;

    if (!(frame->can_id & CAN_ERR_FLAG))
        return;

    if (eid & CAN_ERR_TX_TIMEOUT)
        printf("TX timeout\n");

    if (eid & CAN_ERR_LOSTARB)
        printf("Arbitration kaybedildi: bit %d\n", frame->data[0]);

    if (eid & CAN_ERR_CRTL) {
        printf("Controller hatası:");
        if (frame->data[1] & CAN_ERR_CRTL_RX_OVERFLOW)
            printf(" RX overflow");
        if (frame->data[1] & CAN_ERR_CRTL_TX_PASSIVE)
            printf(" TX passive");
        if (frame->data[1] & CAN_ERR_CRTL_RX_PASSIVE)
            printf(" RX passive");
        printf("\n");
    }

    if (eid & CAN_ERR_PROT) {
        printf("Protokol hatası:");
        if (frame->data[2] & CAN_ERR_PROT_BIT)
            printf(" bit error");
        if (frame->data[2] & CAN_ERR_PROT_STUFF)
            printf(" stuff error");
        if (frame->data[2] & CAN_ERR_PROT_FORM)
            printf(" form error");
        if (frame->data[2] & CAN_ERR_PROT_ACK)
            printf(" ACK error");
        printf("\n");
    }

    if (eid & CAN_ERR_BUSOFF)
        printf("BUS-OFF — arayüzü yeniden başlat\n");

    printf("TEC=%d, REC=%d\n", frame->data[6], frame->data[7]);
}

Üretim ipuçları

KonuÖneri
TerminationCAN bus'un her iki ucuna 120 Ω direnç bağlanmalıdır. Eksik termination refleksiyona yol açar; yüksek hızlarda özellikle kritiktir.
Baud rate toleransıCAN, ±2.5% saat toleransını kaldırır. Farklı üreticilerin node'ları ile çalışırken sample-point değerlerinin uyumlu olduğundan emin olun.
Kablo uzunluğu1 Mbit/s için maksimum 40 m. Hız düştükçe uzunluk artar: 125 kbit/s → 500 m. Kablo kapasitansı kritik parametredir.
EMICAN_H ve CAN_L hatları bükümlü çift (twisted pair) olmalıdır. Yüksek akım devrelerinden (motor sürücüleri) ayrı tutulmalıdır.
Bus-off recoveryÜretim sistemlerinde restart-ms sonsuz döngüyü önlemek için backoff algoritması ile kullanılmalıdır. Kök neden çözülmeden recovery döngüsü zararlıdır.
TimestampSO_TIMESTAMP veya SO_TIMESTAMPING ile hardware timestamp kullanın. Software timestamp ±ms sapma üretebilir; log analizinde yanıltıcı olur.

Hardware timestamp

can_hwtimestamp.c — hardware timestamp etkinleştirme
#include <linux/net_tstamp.h>

/* Hardware timestamp talep et */
const int ts_flags = SOF_TIMESTAMPING_RX_HARDWARE |
                     SOF_TIMESTAMPING_RAW_HARDWARE;
setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
           &ts_flags, sizeof(ts_flags));

/* recvmsg() ile timestamp'i ancillary data olarak al */
struct msghdr  mhdr;
struct iovec   iov;
struct can_frame frame;
char ctrlbuf[128];

iov.iov_base = &frame;
iov.iov_len  = sizeof(frame);

memset(&mhdr, 0, sizeof(mhdr));
mhdr.msg_iov        = &iov;
mhdr.msg_iovlen     = 1;
mhdr.msg_control    = ctrlbuf;
mhdr.msg_controllen = sizeof(ctrlbuf);

recvmsg(sock, &mhdr, 0);

struct cmsghdr *cmsg;
for (cmsg = CMSG_FIRSTHDR(&mhdr); cmsg; cmsg = CMSG_NXTHDR(&mhdr, cmsg)) {
    if (cmsg->cmsg_level == SOL_SOCKET &&
        cmsg->cmsg_type  == SO_TIMESTAMPING) {
        struct timespec *ts = (struct timespec *)CMSG_DATA(cmsg);
        /* ts[2] = hardware timestamp */
        printf("HW TS: %ld.%09ld\n", ts[2].tv_sec, ts[2].tv_nsec);
    }
}
NOT

Hardware timestamp yalnızca destekleyen CAN controller driver'larında mevcuttur (flexcan, c_can, bazı USB adaptörleri). ethtool -T can0 komutu ile hangi timestamp modlarının desteklendiği kontrol edilebilir. Desteklenmeyen modlar için kernel otomatik olarak software timestamp'e geri döner.