Curieux.JY
  • Projects
  • Post
  • Note
  • JungYeon Lee

On this page

  • 배경
  • 문제 상황
  • 사전 준비
  • 1단계: 현재 연결된 CAN 장치 확인
  • 2단계: USB 포트의 물리적 경로(ID_PATH_TAG) 찾기
  • 3단계: Python 스크립트로 CAN 인터페이스 설정
    • 스크립트 주요 기능 설명
    • 스크립트 실행 흐름
  • 4단계: 설정 확인
  • 대안: udev 규칙 사용하기
  • 트러블슈팅
    • 인터페이스가 보이지 않는 경우
    • 권한 문제
    • bitrate 설정 실패
  • 참고 자료

📝CAN Setup

linux
can
2025
device 장치 CAN 포트 고정하기
Published

November 25, 2025

배경

CAN (Controller Area Network)은 차량 및 산업용 장비에서 마이크로컨트롤러와 장치들이 호스트 컴퓨터 없이 서로 통신할 수 있게 해주는 직렬 통신 프로토콜이다. Linux에서는 SocketCAN이라는 네트워크 계층 구현을 통해 CAN 장치를 일반 네트워크 인터페이스처럼 다룰 수 있다.

문제 상황

USB-CAN 어댑터 (예: PEAK CAN)를 여러 개 사용할 때, 장치가 연결될 때마다 can0, can1 등의 이름이 연결 순서에 따라 임의로 할당된다. 시리얼 넘버가 있는 장치라면 udev 규칙으로 고유 이름을 지정할 수 있지만, 시리얼 넘버가 없는 CAN 장치의 경우 물리적인 USB 포트 위치를 기반으로 식별해야 한다.

이 문서에서는 USB 포트의 물리적 경로(ID_PATH_TAG)를 이용해 특정 USB 포트에 연결된 CAN 장치에 고정된 이름을 부여하는 방법을 설명한다.

사전 준비

필요한 패키지들이 설치되어 있는지 확인한다:

# SocketCAN 관련 도구
sudo apt install can-utils

# 네트워크 도구 (ip 명령어)
sudo apt install iproute2

1단계: 현재 연결된 CAN 장치 확인

먼저 CAN 장치가 시스템에 인식되었는지 확인한다.

# USB 장치 목록에서 CAN 어댑터 확인
lsusb | grep -i can
# 예시 출력: Bus 001 Device 005: ID 0c72:000c PEAK System PCAN-USB

# 네트워크 인터페이스로 등록된 CAN 장치 확인
ip link show type can
# 또는
ifconfig -a | grep can

2단계: USB 포트의 물리적 경로(ID_PATH_TAG) 찾기

udevadm 명령어를 사용해 CAN 장치가 연결된 USB 포트의 고유한 물리적 경로를 찾는다:

udevadm info /sys/class/net/can*

출력 결과에서 ID_PATH_TAG 값을 찾는다:

# 예시 출력 (일부)
E: ID_PATH=pci-0000:00:14.0-usb-0:9.1:1.0
E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_9_1_0
E: ID_USB_DRIVER=peak_usb

ID_PATH_TAG 해석:

  • pci-0000_00_14_0: PCI 버스상의 USB 호스트 컨트롤러 위치
  • usb-0_9_1_0: USB 허브/포트 경로 (0번 버스, 9번 포트, 1번 서브포트…)

이 경로는 물리적인 USB 포트 위치를 나타내므로, 항상 같은 포트에 장치를 연결하면 동일한 ID_PATH_TAG 값을 얻게 된다.

여러 CAN 장치가 연결된 경우, 각 장치의 경로를 개별적으로 확인할 수 있다:

# 특정 인터페이스의 상세 정보
udevadm info /sys/class/net/can0
udevadm info /sys/class/net/can1

3단계: Python 스크립트로 CAN 인터페이스 설정

위에서 찾은 ID_PATH_TAG를 사용해 CAN 장치에 원하는 이름을 부여하고 활성화하는 스크립트다. 위에서 찾은 ID_PATH_TAG를 MY_TAG에 지정한다.

import subprocess
import os
import sys

def run_command(cmd):
    """
    Executes a shell command and raises an exception if an error occurs.
    """
    try:
        # Print the command being executed for debugging
        print(f"Executing: {cmd}")
        subprocess.run(cmd, shell=True, check=True, text=True)
    except subprocess.CalledProcessError as e:
        print(f"Error executing command: {cmd}")
        print(f"Return code: {e.returncode}")
        raise e

def find_interface_by_path_tag(target_tag):
    """
    Searches all interfaces in /sys/class/net/ to find the device matching the ID_PATH_TAG.
    (It searches the entire directory to find devices that may have already been renamed)
    """
    net_path = "/sys/class/net"
    
    if not os.path.exists(net_path):
        return None

    # Iterate through all network interfaces (excluding loopback 'lo')
    for iface in os.listdir(net_path):
        if iface == "lo": continue
            
        sys_path = os.path.join(net_path, iface)
        
        try:
            # Execute udevadm info (options removed for backward compatibility)
            cmd = ["udevadm", "info", sys_path]
            result = subprocess.run(cmd, capture_output=True, text=True, check=False)
            
            # Parse output line by line
            for line in result.stdout.splitlines():
                if "ID_PATH_TAG=" in line:
                    parts = line.split("=", 1)
                    if len(parts) == 2:
                        current_tag = parts[1].strip()
                        if current_tag == target_tag:
                            return iface
        except:
            continue

    return None

def setup_can_interface(target_tag, new_name, bitrate=1000000):
    """
    Finds the CAN device by tag, renames it, and brings it up.
    """
    print(f"--- Starting CAN device setup (Target: {new_name}) ---")

    # 1. Find current device name
    current_name = find_interface_by_path_tag(target_tag)
    
    if not current_name:
        print(f"[Failed] Could not find device matching tag ({target_tag}).")
        return False

    print(f"Device found: Current name is '{current_name}'.")

    try:
        # 2. Bring interface down (Required before renaming or changing type)
        run_command(f"sudo ip link set {current_name} down")

        # 3. Rename interface
        # Only rename if the current name differs from the target name
        if current_name != new_name:
            print(f"Attempting to rename: {current_name} -> {new_name}")
            run_command(f"sudo ip link set {current_name} name {new_name}")
        else:
            print(f"Name is already {new_name}. Skipping rename.")

        # 4. Set bitrate (Type CAN)
        # Use new_name since the interface has been renamed
        run_command(f"sudo ip link set {new_name} type can bitrate {bitrate}")

        # 5. Bring interface up
        run_command(f"sudo ip link set {new_name} up")
        
        print(f"\n[Success] Device {new_name} is up with bitrate {bitrate}bps.")
        return True

    except Exception as e:
        print(f"\n[Error] An issue occurred during setup: {e}")
        return False

# --- Usage Example ---
if __name__ == "__main__":
    # 1. Tag of the device to find (obtained from udevadm info)
    #   - $ udevadm info /sys/class/net/can* 
    #     ID_PATH_TAG=pci-0000_00_14_0-usb-0_9_1_0 
    MY_TAG = "pci-0000_00_14_0-usb-0_9_1_0"
    
    # 2. Target name and bitrate
    TARGET_NAME = "can_sensor_x"
    TARGET_BITRATE = 1000000
    
    # Run function
    setup_can_interface(MY_TAG, TARGET_NAME, TARGET_BITRATE)

스크립트 주요 기능 설명

함수 설명
find_interface_by_path_tag() /sys/class/net/ 하위의 모든 네트워크 인터페이스를 순회하며 ID_PATH_TAG가 일치하는 장치를 찾는다
setup_can_interface() 장치를 찾아 이름을 변경하고, bitrate를 설정한 후 활성화한다

스크립트 실행 흐름

1. ID_PATH_TAG로 현재 인터페이스 이름 찾기 (예: can0)
          ↓
2. 인터페이스 비활성화 (ip link set down)
          ↓
3. 인터페이스 이름 변경 (ip link set name)
          ↓
4. CAN bitrate 설정 (ip link set type can bitrate)
          ↓
5. 인터페이스 활성화 (ip link set up)

4단계: 설정 확인

스크립트 실행 후 설정이 제대로 되었는지 확인한다:

# 인터페이스 상태 확인
ip -details link show can_sensor_x

# CAN 통신 테스트 (can-utils 필요)
candump can_sensor_x        # 수신 메시지 모니터링
cansend can_sensor_x 123#DEADBEEF  # 테스트 메시지 전송

대안: udev 규칙 사용하기

매번 스크립트를 실행하는 대신, udev 규칙을 만들어 자동화할 수도 있다:

# /etc/udev/rules.d/99-can-naming.rules

# USB 포트 경로 기반 CAN 인터페이스 이름 지정
SUBSYSTEM=="net", ACTION=="add", ENV{ID_PATH_TAG}=="pci-0000_00_14_0-usb-0_9_1_0", NAME="can_sensor_x"

규칙 적용:

sudo udevadm control --reload-rules
sudo udevadm trigger

단, udev 규칙만으로는 bitrate 설정과 인터페이스 활성화가 되지 않으므로, systemd 서비스와 조합하거나 위 Python 스크립트를 시작 시 실행하는 방식을 사용해야 한다.

트러블슈팅

인터페이스가 보이지 않는 경우

# 커널 모듈 확인
lsmod | grep can
# peak_usb, can_dev, can 모듈이 로드되어야 함

# 모듈 수동 로드
sudo modprobe peak_usb

권한 문제

스크립트는 sudo 권한이 필요하다. 일반 사용자로 실행하려면:

# 현재 사용자를 dialout 그룹에 추가 (로그아웃 후 적용)
sudo usermod -aG dialout $USER

bitrate 설정 실패

장치가 지원하지 않는 bitrate를 설정하면 에러가 발생한다. 일반적인 CAN bitrate:

  • 125000 (125 kbps)
  • 250000 (250 kbps)
  • 500000 (500 kbps)
  • 1000000 (1 Mbps)

참고 자료

  • Linux SocketCAN 공식 문서
  • PEAK-System Linux Driver
  • can-utils GitHub

Copyright 2026, JungYeon Lee