CAN/CAN 기초

Linux에서 SocketCAN을 활용한 CAN 네트워크 제어

임베디드 친구 2025. 3. 11. 08:46
728x90
반응형

Linux에서 SocketCAN을 활용한 CAN 네트워크 제어

1. 개요

CAN(Controller Area Network)은 자동차, 산업 자동화, 로봇 제어 등 다양한 분야에서 사용되는 직렬 통신 프로토콜입니다. Linux에서는 SocketCAN을 통해 CAN 네트워크를 제어할 수 있으며, 이는 기존의 네트워크 소켓 API와 유사한 방식으로 동작합니다.

이번 포스팅에서는 Linux의 CAN 드라이버 구조 및 설정, SocketCAN API를 활용한 데이터 송수신, 그리고 Python과 C를 활용한 CAN 통신 프로그래밍 방법을 자세히 살펴보겠습니다.


2. Linux의 CAN 드라이버 구조 및 설정

2.1 CAN 드라이버 개요

Linux 커널은 SocketCAN을 통해 CAN 인터페이스를 네트워크 장치로 지원합니다. 이를 통해 CAN 인터페이스를 마치 이더넷 인터페이스처럼 다룰 수 있습니다.

2.2 CAN 드라이버 로드

CAN 인터페이스를 활성화하려면 다음과 같은 모듈을 로드해야 합니다.

sudo modprobe can
sudo modprobe can_raw
sudo modprobe can_bcm
sudo modprobe vcan  # 가상 CAN 인터페이스 사용 시 필요

위 명령을 실행하면 SocketCAN을 사용하기 위한 핵심 모듈이 로드됩니다.

2.3 CAN 인터페이스 활성화

일반적으로 CAN 인터페이스는 can0, can1 등의 이름을 갖습니다. 인터페이스를 활성화하려면 다음과 같은 명령을 사용합니다.

sudo ip link set can0 type can bitrate 500000  # CAN 속도 설정 (500kbps)
sudo ip link set up can0  # 인터페이스 활성화

가상 CAN 인터페이스(vcan)를 설정하려면 아래 명령을 실행합니다.

sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0

설정된 인터페이스를 확인하려면 다음 명령을 사용합니다.

ip -details link show can0

인터페이스를 중지하려면 아래 명령을 실행합니다.

sudo ip link set down can0

3. SocketCAN API를 활용한 데이터 송수신

Linux에서는 candumpcansend 명령어를 이용해 CAN 메시지를 송수신할 수 있습니다.

3.1 CAN 메시지 수신 (candump)

다음 명령을 실행하면 can0 인터페이스에서 수신되는 모든 CAN 메시지를 출력합니다.

candump can0

3.2 CAN 메시지 송신 (cansend)

특정 메시지를 전송하려면 아래 명령을 사용합니다.

cansend can0 123#DEADBEEF  # ID: 0x123, 데이터: 0xDEADBEEF

3.3 CAN 메시지 필터링

특정 ID의 메시지만 수신하려면 아래와 같이 실행합니다.

candump can0,123:7FF  # ID 0x123인 메시지만 출력

4. Python과 C를 활용한 CAN 통신 프로그래밍

4.1 Python을 활용한 CAN 송수신

Python에서는 python-can 라이브러리를 활용하여 CAN 메시지를 송수신할 수 있습니다.

(1) 라이브러리 설치

pip install python-can

(2) CAN 메시지 송신 예제

import can

bus = can.interface.Bus(channel='can0', bustype='socketcan')
msg = can.Message(arbitration_id=0x123, data=[0xDE, 0xAD, 0xBE, 0xEF], is_extended_id=False)

try:
    bus.send(msg)
    print("메시지를 전송했습니다.")
except can.CanError:
    print("메시지 전송 실패!")

(3) CAN 메시지 수신 예제

import can

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

print("CAN 메시지 수신 대기 중...")
while True:
    msg = bus.recv()
    print(f"수신된 메시지: ID={hex(msg.arbitration_id)}, 데이터={msg.data}")

4.2 C 언어를 활용한 CAN 송수신

(1) CAN 송신 코드 (C)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <sys/ioctl.h>

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

    sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    strcpy(ifr.ifr_name, "can0");
    ioctl(sock, SIOCGIFINDEX, &ifr);
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    bind(sock, (struct sockaddr *)&addr, sizeof(addr));

    frame.can_id = 0x123;
    frame.can_dlc = 4;
    frame.data[0] = 0xDE;
    frame.data[1] = 0xAD;
    frame.data[2] = 0xBE;
    frame.data[3] = 0xEF;

    write(sock, &frame, sizeof(struct can_frame));
    printf("메시지를 전송했습니다.\n");
    close(sock);

    return 0;
}

컴파일 후 실행:

gcc -o can_send can_send.c
sudo ./can_send

(2) CAN 수신 코드 (C)

int sock;
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame;

sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
strcpy(ifr.ifr_name, "can0");
ioctl(sock, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
bind(sock, (struct sockaddr *)&addr, sizeof(addr));

while (1) {
    read(sock, &frame, sizeof(struct can_frame));
    printf("수신된 메시지: ID=%X, 데이터=%X\n", frame.can_id, frame.data[0]);
}

close(sock);

5. 결론

이번 포스팅에서는 Linux에서 SocketCAN을 활용하여 CAN 네트워크를 설정하고, 명령어 및 Python, C 코드를 활용하여 CAN 메시지를 송수신하는 방법을 알아보았습니다. 실제 프로젝트에서는 이러한 기법을 기반으로 CAN 프로토콜을 구현하여 다양한 하드웨어 장치와 통신할 수 있습니다.

728x90
반응형